25 __all__ = [
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"CRITICAL",
"WARNING",
26 "Log",
"configure",
"configure_prop",
"configure_pylog_MDC",
"getDefaultLogger",
27 "getLogger",
"MDC",
"MDCDict",
"MDCRemove",
"MDCRegisterInit",
"setLevel",
28 "getLevel",
"isEnabledFor",
"log",
"trace",
"debug",
"info",
"warn",
"warning",
29 "error",
"fatal",
"critical",
"logf",
"tracef",
"debugf",
"infof",
"warnf",
"errorf",
"fatalf",
30 "lwpID",
"usePythonLogging",
"doNotUsePythonLogging",
"UsePythonLogging",
31 "LevelTranslator",
"LogHandler",
"getEffectiveLevel",
"getLevelName"]
37 from typing
import Optional
38 from deprecated.sphinx
import deprecated
58 UsePythonLogging =
False
59 """Forward Python `lsst.log` messages to Python `logging` package."""
66 """Forward log messages to Python `logging`
70 This is useful for unit testing when you want to ensure
71 that log messages are captured by the testing environment
72 as distinct from standard output.
74 This state only affects messages sent to the `lsst.log`
81 """Forward log messages to LSST logging system.
85 This is the default state.
95 return self.getLevel()
99 """Returns the parent logger, or None if this is the root logger."""
100 if not self.
namename:
102 parent_name = self.
namename.rpartition(
".")[0]
104 return self.getDefaultLogger()
105 return self.getLogger(parent_name)
108 self.
_log_log(Log.TRACE,
False, fmt, *args)
111 self.
_log_log(Log.DEBUG,
False, fmt, *args)
114 self.
_log_log(Log.INFO,
False, fmt, *args)
117 self.
_log_log(Log.WARN,
False, fmt, *args)
122 self.
_log_log(Log.WARN,
False, fmt, *args)
125 self.
_log_log(Log.ERROR,
False, fmt, *args)
128 self.
_log_log(Log.FATAL,
False, fmt, *args)
133 self.
_log_log(Log.FATAL,
False, fmt, *args)
135 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
137 self.
_log_log(Log.TRACE,
True, fmt, *args, **kwargs)
139 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
141 self.
_log_log(Log.DEBUG,
True, fmt, *args, **kwargs)
143 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
144 def infof(self, fmt, *args, **kwargs):
145 self.
_log_log(Log.INFO,
True, fmt, *args, **kwargs)
147 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
148 def warnf(self, fmt, *args, **kwargs):
149 self.
_log_log(Log.WARN,
True, fmt, *args, **kwargs)
151 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
153 self.
_log_log(Log.ERROR,
True, fmt, *args, **kwargs)
155 @deprecated(reason="f-string log messages are now deprecated to match python logging convention."
" Will be removed after v25",
version="v23.0", category=FutureWarning)
157 self.
_log_log(Log.FATAL,
True, fmt, *args, **kwargs)
159 def _log(self, level, use_format, fmt, *args, **kwargs):
160 if self.isEnabledFor(level):
161 frame = inspect.currentframe().f_back
163 filename = os.path.split(frame.f_code.co_filename)[1]
164 funcname = frame.f_code.co_name
166 msg = fmt.format(*args, **kwargs)
if args
or kwargs
else fmt
168 msg = fmt % args
if args
else fmt
170 levelno = LevelTranslator.lsstLog2logging(level)
171 levelName = logging.getLevelName(levelno)
173 pylog = logging.getLogger(self.getName())
174 record = logging.makeLogRecord(dict(name=self.getName(),
180 pathname=frame.f_code.co_filename,
181 lineno=frame.f_lineno))
184 self.logMsg(level, filename, funcname, frame.f_lineno, msg)
187 """Implement pickle support.
189 args = (self.getName(), )
191 return (getLogger, args)
196 class_name = f
"{cls.__module__}.{cls.__qualname__}"
197 prefix =
"lsst.log.log.log"
198 if class_name.startswith(prefix):
199 class_name = class_name.replace(prefix,
"lsst.log")
200 return f
"<{class_name} '{self.name}' ({getLevelName(self.getEffectiveLevel())})>"
204 """Dictionary for MDC data.
206 This is internal class used for better formatting of MDC in Python logging
207 output. It behaves like `defaultdict(str)` but overrides ``__str__`` and
208 ``__repr__`` method to produce output better suited for logging records.
210 def __getitem__(self, name: str):
211 """Returns value for a given key or empty string for missing key.
213 return self.get(name,
"")
216 """Return string representation, strings are interpolated without
219 items = (f
"{k}={self[k]}" for k
in sorted(self))
220 return "{" +
", ".join(items) +
"}"
234 Log.configure_prop(properties)
238 """Configure log4cxx to send messages to Python logging, with MDC support.
243 Name of the logging level for root log4cxx logger.
244 MDC_class : `type`, optional
245 Type of dictionary which is added to `logging.LogRecord` as an ``MDC``
246 attribute. Any dictionary or ``defaultdict``-like class can be used as
247 a type. If `None` the `logging.LogRecord` will not be augmented.
251 This method does two things:
253 - Configures log4cxx with a given logging level and a ``PyLogAppender``
254 appender class which forwards all messages to Python `logging`.
255 - Installs a record factory for Python `logging` that adds ``MDC``
256 attribute to every `logging.LogRecord` object (instance of
257 ``MDC_class``). This will happen by default but can be disabled
258 by setting the ``MDC_class`` parameter to `None`.
260 if MDC_class
is not None:
261 old_factory = logging.getLogRecordFactory()
263 def record_factory(*args, **kwargs):
264 record = old_factory(*args, **kwargs)
265 record.MDC = MDC_class()
268 logging.setLogRecordFactory(record_factory)
271 log4j.rootLogger = {}, PyLog
272 log4j.appender.PyLog = PyLogAppender
278 return Log.getDefaultLogger()
282 return Log.getLogger(loggername)
286 return Log.MDC(key, str(value))
294 Log.MDCRegisterInit(func)
298 Log.getLogger(loggername).
setLevel(level)
302 return Log.getLogger(loggername).
getLevel()
316 def log(loggername, level, fmt, *args, **kwargs):
317 Log.getLogger(loggername)._log(level,
False, fmt, *args)
320 def trace(fmt, *args):
321 Log.getDefaultLogger()._log(TRACE,
False, fmt, *args)
324 def debug(fmt, *args):
325 Log.getDefaultLogger()._log(DEBUG,
False, fmt, *args)
329 Log.getDefaultLogger()._log(INFO,
False, fmt, *args)
333 Log.getDefaultLogger()._log(WARN,
False, fmt, *args)
341 Log.getDefaultLogger()._log(ERROR,
False, fmt, *args)
345 Log.getDefaultLogger()._log(FATAL,
False, fmt, *args)
352 def logf(loggername, level, fmt, *args, **kwargs):
353 Log.getLogger(loggername)._log(level,
True, fmt, *args, **kwargs)
357 Log.getDefaultLogger()._log(TRACE,
True, fmt, *args, **kwargs)
361 Log.getDefaultLogger()._log(DEBUG,
True, fmt, *args, **kwargs)
365 Log.getDefaultLogger()._log(INFO,
True, fmt, *args, **kwargs)
369 Log.getDefaultLogger()._log(WARN,
True, fmt, *args, **kwargs)
373 Log.getDefaultLogger()._log(ERROR,
True, fmt, *args, **kwargs)
377 Log.getDefaultLogger()._log(FATAL,
True, fmt, *args, **kwargs)
385 """Return the name associated with this logging level.
387 Returns "Level %d" if no name can be found.
389 names = (
"DEBUG",
"TRACE",
"WARNING",
"FATAL",
"INFO",
"ERROR")
391 test_level = getattr(Log, name)
392 if test_level == level:
394 return f
"Level {level}"
402 Log.usePythonLogging()
406 Log.doNotUsePythonLogging()
410 """Context manager to enable Python log forwarding temporarily.
414 self.current = Log.UsePythonLogging
417 Log.usePythonLogging()
419 def __exit__(self, exc_type, exc_value, traceback):
420 Log.UsePythonLogging = self.current
424 """Helper class to translate levels between ``lsst.log`` and Python
428 def lsstLog2logging(level):
429 """Translates from lsst.log/log4cxx levels to `logging` module levels.
434 Logging level number used by `lsst.log`, typically one of the
435 constants defined in this module (`DEBUG`, `INFO`, etc.)
440 Correspoding logging level number for Python `logging` module.
449 """Translates from standard python `logging` module levels to
450 lsst.log/log4cxx levels.
455 Logging level number used by Python `logging`, typically one of
456 the constants defined by `logging` module (`logging.DEBUG`,
457 `logging.INFO`, etc.)
462 Correspoding logging level number for `lsst.log` module.
468 """Handler for Python logging module that emits to LSST logging.
473 Level at which to set the this handler.
477 If this handler is enabled and `lsst.log` has been configured to use
478 Python `logging`, the handler will do nothing itself if any other
479 handler has been registered with the Python logger. If it does not
480 think that anything else is handling the message it will attempt to
481 send the message via a default `~logging.StreamHandler`. The safest
482 approach is to configure the logger with an additional handler
483 (possibly the ROOT logger) if `lsst.log` is to be configured to use
487 def __init__(self, level=logging.NOTSET):
488 logging.Handler.__init__(self, level=level)
491 self.
formatterformatter = logging.Formatter(fmt=
"%(message)s")
494 logger = Log.getLogger(record.name)
495 if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
496 logging.Handler.handle(self, record)
498 def emit(self, record):
499 if Log.UsePythonLogging:
505 pylgr = logging.getLogger(record.name)
509 if any(
not isinstance(h, self.__class__)
for h
in pylgr.handlers):
515 if pylgr.parent
and pylgr.parent.hasHandlers()
and pylgr.propagate:
521 stream = logging.StreamHandler()
522 stream.setFormatter(logging.Formatter(fmt=
"%(name)s %(levelname)s (fallback): %(message)s"))
523 stream.handle(record)
526 logger = Log.getLogger(record.name)
530 logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
531 record.filename, record.funcName,
532 record.lineno, message)
533
def logging2lsstLog(level)
def __init__(self, level=logging.NOTSET)
def fatal(self, fmt, *args)
def trace(self, fmt, *args)
def debugf(self, fmt, *args, **kwargs)
def warn(self, fmt, *args)
def tracef(self, fmt, *args, **kwargs)
def error(self, fmt, *args)
def warning(self, fmt, *args)
def infof(self, fmt, *args, **kwargs)
def doNotUsePythonLogging(cls)
def critical(self, fmt, *args)
def _log(self, level, use_format, fmt, *args, **kwargs)
def errorf(self, fmt, *args, **kwargs)
def usePythonLogging(cls)
def debug(self, fmt, *args)
def info(self, fmt, *args)
def fatalf(self, fmt, *args, **kwargs)
def warnf(self, fmt, *args, **kwargs)
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def doNotUsePythonLogging()
def isEnabledFor(loggername, level)
def MDCRegisterInit(func)
def setLevel(loggername, level)
def getLogger(loggername)
def fatalf(fmt, *args, **kwargs)
def tracef(fmt, *args, **kwargs)
def logf(loggername, level, fmt, *args, **kwargs)
def debugf(fmt, *args, **kwargs)
def configure_pylog_MDC(str level, Optional[type] MDC_class=MDCDict)
def infof(fmt, *args, **kwargs)
def log(loggername, level, fmt, *args, **kwargs)
def warnf(fmt, *args, **kwargs)
def errorf(fmt, *args, **kwargs)
def configure_prop(properties)
def getEffectiveLevel(loggername)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)