Coverage for python/lsst/log/log/logContinued.py : 43%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3#
4# LSST Data Management System
5# Copyright 2013 LSST Corporation.
6#
7# This product includes software developed by the
8# LSST Project (http://www.lsst.org/).
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the LSST License Statement and
21# the GNU General Public License along with this program. If not,
22# see <http://www.lsstcorp.org/LegalNotices/>.
23#
25__all__ = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL",
26 "Log", "configure", "configure_prop", "getDefaultLogger", "getLogger",
27 "MDC", "MDCRemove", "MDCRegisterInit", "setLevel", "getLevel", "isEnabledFor",
28 "log", "trace", "debug", "info", "warn", "warning", "error", "fatal", "logf",
29 "tracef", "debugf", "infof", "warnf", "errorf", "fatalf", "lwpID",
30 "usePythonLogging", "doNotUsePythonLogging", "UsePythonLogging",
31 "LevelTranslator", "LogHandler"]
33import logging
34import inspect
35import os
37from lsst.utils import continueClass
39from .log import Log
41TRACE = 5000
42DEBUG = 10000
43INFO = 20000
44WARN = 30000
45ERROR = 40000
46FATAL = 50000
49@continueClass # noqa F811 redefinition
50class Log:
51 UsePythonLogging = False
52 """Forward Python `lsst.log` messages to Python `logging` package."""
54 @classmethod
55 def usePythonLogging(cls):
56 """Forward log messages to Python `logging`
58 Notes
59 -----
60 This is useful for unit testing when you want to ensure
61 that log messages are captured by the testing environment
62 as distinct from standard output.
64 This state only affects messages sent to the `lsst.log`
65 package from Python.
66 """
67 cls.UsePythonLogging = True
69 @classmethod
70 def doNotUsePythonLogging(cls):
71 """Forward log messages to LSST logging system.
73 Notes
74 -----
75 This is the default state.
76 """
77 cls.UsePythonLogging = False
79 def trace(self, fmt, *args):
80 self._log(Log.TRACE, False, fmt, *args)
82 def debug(self, fmt, *args):
83 self._log(Log.DEBUG, False, fmt, *args)
85 def info(self, fmt, *args):
86 self._log(Log.INFO, False, fmt, *args)
88 def warn(self, fmt, *args):
89 self._log(Log.WARN, False, fmt, *args)
91 def warning(self, fmt, *args):
92 self.warn(fmt, *args)
94 def error(self, fmt, *args):
95 self._log(Log.ERROR, False, fmt, *args)
97 def fatal(self, fmt, *args):
98 self._log(Log.FATAL, False, fmt, *args)
100 def tracef(self, fmt, *args, **kwargs):
101 self._log(Log.TRACE, True, fmt, *args, **kwargs)
103 def debugf(self, fmt, *args, **kwargs):
104 self._log(Log.DEBUG, True, fmt, *args, **kwargs)
106 def infof(self, fmt, *args, **kwargs):
107 self._log(Log.INFO, True, fmt, *args, **kwargs)
109 def warnf(self, fmt, *args, **kwargs):
110 self._log(Log.WARN, True, fmt, *args, **kwargs)
112 def errorf(self, fmt, *args, **kwargs):
113 self._log(Log.ERROR, True, fmt, *args, **kwargs)
115 def fatalf(self, fmt, *args, **kwargs):
116 self._log(Log.FATAL, True, fmt, *args, **kwargs)
118 def _log(self, level, use_format, fmt, *args, **kwargs):
119 if self.isEnabledFor(level):
120 frame = inspect.currentframe().f_back # calling method
121 frame = frame.f_back # original log location
122 filename = os.path.split(frame.f_code.co_filename)[1]
123 funcname = frame.f_code.co_name
124 if use_format:
125 msg = fmt.format(*args, **kwargs) if args or kwargs else fmt
126 else:
127 msg = fmt % args if args else fmt
128 if self.UsePythonLogging:
129 pylog = logging.getLogger(self.getName())
130 record = logging.LogRecord(self.getName(), LevelTranslator.lsstLog2logging(level),
131 filename, frame.f_lineno, msg, None, False, func=funcname)
132 pylog.handle(record)
133 else:
134 self.logMsg(level, filename, funcname, frame.f_lineno, msg)
136 def __reduce__(self):
137 """Implement pickle support.
138 """
139 args = (self.getName(), )
140 # method has to be module-level, not class method
141 return (getLogger, args)
143# Export static functions from Log class to module namespace
146def configure(*args):
147 Log.configure(*args)
150def configure_prop(properties):
151 Log.configure_prop(properties)
154def getDefaultLogger():
155 return Log.getDefaultLogger()
158def getLogger(loggername):
159 return Log.getLogger(loggername)
162def MDC(key, value):
163 Log.MDC(key, str(value))
166def MDCRemove(key):
167 Log.MDCRemove(key)
170def MDCRegisterInit(func):
171 Log.MDCRegisterInit(func)
174def setLevel(loggername, level):
175 Log.getLogger(loggername).setLevel(level)
178def getLevel(loggername):
179 Log.getLogger(loggername).getLevel()
182def isEnabledFor(logger, level):
183 Log.getLogger(logger).isEnabledFor(level)
186def log(loggername, level, fmt, *args, **kwargs):
187 Log.getLogger(loggername)._log(level, False, fmt, *args)
190def trace(fmt, *args):
191 Log.getDefaultLogger()._log(TRACE, False, fmt, *args)
194def debug(fmt, *args):
195 Log.getDefaultLogger()._log(DEBUG, False, fmt, *args)
198def info(fmt, *args):
199 Log.getDefaultLogger()._log(INFO, False, fmt, *args)
202def warn(fmt, *args):
203 Log.getDefaultLogger()._log(WARN, False, fmt, *args)
206def warning(fmt, *args):
207 warn(fmt, *args)
210def error(fmt, *args):
211 Log.getDefaultLogger()._log(ERROR, False, fmt, *args)
214def fatal(fmt, *args):
215 Log.getDefaultLogger()._log(FATAL, False, fmt, *args)
218def logf(loggername, level, fmt, *args, **kwargs):
219 Log.getLogger(loggername)._log(level, True, fmt, *args, **kwargs)
222def tracef(fmt, *args, **kwargs):
223 Log.getDefaultLogger()._log(TRACE, True, fmt, *args, **kwargs)
226def debugf(fmt, *args, **kwargs):
227 Log.getDefaultLogger()._log(DEBUG, True, fmt, *args, **kwargs)
230def infof(fmt, *args, **kwargs):
231 Log.getDefaultLogger()._log(INFO, True, fmt, *args, **kwargs)
234def warnf(fmt, *args, **kwargs):
235 Log.getDefaultLogger()._log(WARN, True, fmt, *args, **kwargs)
238def errorf(fmt, *args, **kwargs):
239 Log.getDefaultLogger()._log(ERROR, True, fmt, *args, **kwargs)
242def fatalf(fmt, *args, **kwargs):
243 Log.getDefaultLogger()._log(FATAL, True, fmt, *args, **kwargs)
246def lwpID():
247 return Log.lwpID
250def usePythonLogging():
251 Log.usePythonLogging()
254def doNotUsePythonLogging():
255 Log.doNotUsePythonLogging()
258class UsePythonLogging:
259 """Context manager to enable Python log forwarding temporarily.
260 """
262 def __init__(self):
263 self.current = Log.UsePythonLogging
265 def __enter__(self):
266 Log.usePythonLogging()
268 def __exit__(self, exc_type, exc_value, traceback):
269 Log.UsePythonLogging = self.current
272class LevelTranslator:
273 """Helper class to translate levels between ``lsst.log`` and Python
274 `logging`.
275 """
276 @staticmethod
277 def lsstLog2logging(level):
278 """Translates from lsst.log/log4cxx levels to `logging` module levels.
280 Parameters
281 ----------
282 level : `int`
283 Logging level number used by `lsst.log`, typically one of the
284 constants defined in this module (`DEBUG`, `INFO`, etc.)
286 Returns
287 -------
288 level : `int`
289 Correspoding logging level number for Python `logging` module.
290 """
291 # Python logging levels are same as lsst.log divided by 1000,
292 # logging does not have TRACE level by default but it is OK to use
293 # that numeric level and we may even add TRACE later.
294 return level//1000
296 @staticmethod
297 def logging2lsstLog(level):
298 """Translates from standard python `logging` module levels to
299 lsst.log/log4cxx levels.
301 Parameters
302 ----------
303 level : `int`
304 Logging level number used by Python `logging`, typically one of
305 the constants defined by `logging` module (`logging.DEBUG`,
306 `logging.INFO`, etc.)
308 Returns
309 -------
310 level : `int`
311 Correspoding logging level number for `lsst.log` module.
312 """
313 return level*1000
316class LogHandler(logging.Handler):
317 """Handler for Python logging module that emits to LSST logging.
319 Parameters
320 ---------
321 level : `int`
322 Level at which to set the this handler.
324 Notes
325 -----
326 If this handler is enabled and `lsst.log` has been configured to use
327 Python `logging`, the handler will do nothing itself if any other
328 handler has been registered with the Python logger. If it does not
329 think that anything else is handling the message it will attempt to
330 send the message via a default `~logging.StreamHandler`. The safest
331 approach is to configure the logger with an additional handler
332 (possibly the ROOT logger) if `lsst.log` is to be configured to use
333 Python logging.
334 """
336 def __init__(self, level=logging.NOTSET):
337 logging.Handler.__init__(self, level=level)
338 # Format as a simple message because lsst.log will format the
339 # message a second time.
340 self.formatter = logging.Formatter(fmt="%(message)s")
342 def handle(self, record):
343 logger = Log.getLogger(record.name)
344 if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
345 logging.Handler.handle(self, record)
347 def emit(self, record):
348 if Log.UsePythonLogging:
349 # Do not forward this message to lsst.log since this may cause
350 # a logging loop.
352 # Work out whether any other handler is going to be invoked
353 # for this logger.
354 pylgr = logging.getLogger(record.name)
356 # If another handler is registered that is not LogHandler
357 # we ignore this request
358 if any(not isinstance(h, self.__class__) for h in pylgr.handlers):
359 return
361 # If the parent has handlers and propagation is enabled
362 # we punt as well (and if a LogHandler is involved then we will
363 # ask the same question when we get to it).
364 if pylgr.parent and pylgr.parent.hasHandlers() and pylgr.propagate:
365 return
367 # Force this message to appear somewhere.
368 # If something else should happen then the caller should add a
369 # second Handler.
370 stream = logging.StreamHandler()
371 stream.setFormatter(logging.Formatter(fmt="%(name)s %(levelname)s (fallback): %(message)s"))
372 stream.handle(record)
373 return
375 logger = Log.getLogger(record.name)
376 # Use standard formatting class to format message part of the record
377 message = self.formatter.format(record)
379 logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
380 record.filename, record.funcName,
381 record.lineno, message)