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",
30 "lwpID",
"usePythonLogging",
"doNotUsePythonLogging",
"UsePythonLogging",
31 "LevelTranslator",
"LogHandler",
"getEffectiveLevel",
"getLevelName"]
37from typing
import Optional
57 UsePythonLogging =
False
58 """Forward Python `lsst.log` messages to Python `logging` package."""
65 """Forward log messages to Python `logging`
69 This is useful
for unit testing when you want to ensure
70 that log messages are captured by the testing environment
71 as distinct
from standard output.
73 This state only affects messages sent to the `
lsst.log`
80 """Forward log messages to LSST logging system.
84 This is the default state.
94 return self.getLevel()
98 """Returns the parent logger, or None if this is the root logger."""
101 parent_name = self.
name.rpartition(
".")[0]
103 return self.getDefaultLogger()
104 return self.getLogger(parent_name)
107 self.
_log(Log.TRACE,
False, fmt, *args)
110 self.
_log(Log.DEBUG,
False, fmt, *args)
113 self.
_log(Log.INFO,
False, fmt, *args)
116 self.
_log(Log.WARN,
False, fmt, *args)
121 self.
_log(Log.WARN,
False, fmt, *args)
124 self.
_log(Log.ERROR,
False, fmt, *args)
127 self.
_log(Log.FATAL,
False, fmt, *args)
132 self.
_log(Log.FATAL,
False, fmt, *args)
134 def _log(self, level, use_format, fmt, *args, **kwargs):
135 if self.isEnabledFor(level):
136 frame = inspect.currentframe().f_back
138 filename = os.path.split(frame.f_code.co_filename)[1]
139 funcname = frame.f_code.co_name
141 msg = fmt.format(*args, **kwargs)
if args
or kwargs
else fmt
143 msg = fmt % args
if args
else fmt
145 levelno = LevelTranslator.lsstLog2logging(level)
146 levelName = logging.getLevelName(levelno)
148 pylog = logging.getLogger(self.getName())
149 record = logging.makeLogRecord(dict(name=self.getName(),
155 pathname=frame.f_code.co_filename,
156 lineno=frame.f_lineno))
159 self.logMsg(level, filename, funcname, frame.f_lineno, msg)
162 """Implement pickle support.
164 args = (self.getName(), )
166 return (getLogger, args)
171 class_name = f
"{cls.__module__}.{cls.__qualname__}"
172 prefix =
"lsst.log.log.log"
173 if class_name.startswith(prefix):
174 class_name = class_name.replace(prefix,
"lsst.log")
175 return f
"<{class_name} '{self.name}' ({getLevelName(self.getEffectiveLevel())})>"
179 """Dictionary for MDC data.
181 This is internal
class used for better formatting of
MDC in Python
logging
182 output. It behaves like `defaultdict(str)` but overrides ``__str__``
and
183 ``__repr__`` method to produce output better suited
for logging records.
186 """Returns value for a given key or empty string for missing key.
188 return self.get(name,
"")
191 """Return string representation, strings are interpolated without
194 items = (f"{k}={self[k]}" for k
in sorted(self))
195 return "{" +
", ".join(items) +
"}"
209 Log.configure_prop(properties)
213 """Configure log4cxx to send messages to Python logging, with MDC support.
218 Name of the logging level for root log4cxx logger.
219 MDC_class : `type`, optional
220 Type of dictionary which
is added to `logging.LogRecord`
as an ``MDC``
221 attribute. Any dictionary
or ``defaultdict``-like
class can be used as
222 a type. If `
None` the `logging.LogRecord` will
not be augmented.
226 This method does two things:
228 - Configures log4cxx
with a given logging level
and a ``PyLogAppender``
229 appender
class which forwards all messages
to Python `logging`.
230 - Installs a record factory
for Python `logging` that adds ``MDC``
231 attribute to every `logging.LogRecord` object (instance of
232 ``MDC_class``). This will happen by default but can be disabled
233 by setting the ``MDC_class`` parameter to `
None`.
235 if MDC_class
is not None:
236 old_factory = logging.getLogRecordFactory()
238 def record_factory(*args, **kwargs):
239 record = old_factory(*args, **kwargs)
240 record.MDC = MDC_class()
243 logging.setLogRecordFactory(record_factory)
246log4j.rootLogger = {}, PyLog
247log4j.appender.PyLog = PyLogAppender
253 return Log.getDefaultLogger()
256def getLogger(loggername):
257 return Log.getLogger(loggername)
261 return Log.MDC(key, str(value))
269 Log.MDCRegisterInit(func)
273 Log.getLogger(loggername).
setLevel(level)
277 return Log.getLogger(loggername).
getLevel()
291def log(loggername, level, fmt, *args, **kwargs):
292 Log.getLogger(loggername)._log(level,
False, fmt, *args)
296 Log.getDefaultLogger()._log(TRACE,
False, fmt, *args)
300 Log.getDefaultLogger()._log(DEBUG,
False, fmt, *args)
304 Log.getDefaultLogger()._log(INFO,
False, fmt, *args)
308 Log.getDefaultLogger()._log(WARN,
False, fmt, *args)
316 Log.getDefaultLogger()._log(ERROR,
False, fmt, *args)
320 Log.getDefaultLogger()._log(FATAL,
False, fmt, *args)
332 """Return the name associated with this logging level.
334 Returns "Level %d" if no name can be found.
336 names = ("DEBUG",
"TRACE",
"WARNING",
"FATAL",
"INFO",
"ERROR")
338 test_level = getattr(Log, name)
339 if test_level == level:
341 return f
"Level {level}"
349 Log.usePythonLogging()
353 Log.doNotUsePythonLogging()
357 """Context manager to enable Python log forwarding temporarily.
364 Log.usePythonLogging()
366 def __exit__(self, exc_type, exc_value, traceback):
367 Log.UsePythonLogging = self.
current
371 """Helper class to translate levels between ``lsst.log`` and Python
376 """Translates from lsst.log/log4cxx levels to `logging` module levels.
381 Logging level number used by `lsst.log`, typically one of the
382 constants defined in this module (`DEBUG`, `INFO`, etc.)
387 Correspoding logging level number
for Python `logging` module.
396 """Translates from standard python `logging` module levels to
402 Logging level number used by Python `logging`, typically one of
403 the constants defined by `logging` module (`logging.DEBUG`,
404 `logging.INFO`, etc.)
409 Correspoding logging level number for `
lsst.log` module.
415 """Handler for Python logging module that emits to LSST logging.
420 Level at which to set the this handler.
424 If this handler is enabled
and `
lsst.log` has been configured to use
425 Python `logging`, the handler will do nothing itself
if any other
426 handler has been registered
with the Python logger. If it does
not
427 think that anything
else is handling the message it will attempt to
428 send the message via a default `~logging.StreamHandler`. The safest
429 approach
is to configure the logger
with an additional handler
430 (possibly the ROOT logger)
if `
lsst.log`
is to be configured to use
435 logging.Handler.__init__(self, level=level)
441 logger = Log.getLogger(record.name)
442 if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
443 logging.Handler.handle(self, record)
446 if Log.UsePythonLogging:
452 pylgr = logging.getLogger(record.name)
456 if any(
not isinstance(h, self.
__class__)
for h
in pylgr.handlers):
462 if pylgr.parent
and pylgr.parent.hasHandlers()
and pylgr.propagate:
468 stream = logging.StreamHandler()
469 stream.setFormatter(logging.Formatter(fmt=
"%(name)s %(levelname)s (fallback): %(message)s"))
470 stream.handle(record)
473 logger = Log.getLogger(record.name)
477 logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
478 record.filename, record.funcName,
479 record.lineno, message)
table::Key< std::string > name
__init__(self, level=logging.NOTSET)
warning(self, fmt, *args)
critical(self, fmt, *args)
_log(self, level, use_format, fmt, *args, **kwargs)
__getitem__(self, str name)
__exit__(self, exc_type, exc_value, traceback)
configure_prop(properties)
getEffectiveLevel(loggername)
configure_pylog_MDC(str level, Optional[type] MDC_class=MDCDict)
isEnabledFor(loggername, level)
setLevel(loggername, level)