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#
25import logging
26import inspect
27import os
29from lsst.utils import continueClass
31from .log import Log
33TRACE = 5000
34DEBUG = 10000
35INFO = 20000
36WARN = 30000
37ERROR = 40000
38FATAL = 50000
41@continueClass # noqa F811 redefinition
42class Log:
43 UsePythonLogging = False
44 """Forward Python `lsst.log` messages to Python `logging` package."""
46 @classmethod
47 def usePythonLogging(cls):
48 """Forward log messages to Python `logging`
50 Notes
51 -----
52 This is useful for unit testing when you want to ensure
53 that log messages are captured by the testing environment
54 as distinct from standard output.
56 This state only affects messages sent to the `lsst.log`
57 package from Python.
58 """
59 cls.UsePythonLogging = True
61 @classmethod
62 def doNotUsePythonLogging(cls):
63 """Forward log messages to LSST logging system.
65 Notes
66 -----
67 This is the default state.
68 """
69 cls.UsePythonLogging = False
71 def trace(self, fmt, *args):
72 self._log(Log.TRACE, False, fmt, *args)
74 def debug(self, fmt, *args):
75 self._log(Log.DEBUG, False, fmt, *args)
77 def info(self, fmt, *args):
78 self._log(Log.INFO, False, fmt, *args)
80 def warn(self, fmt, *args):
81 self._log(Log.WARN, False, fmt, *args)
83 def warning(self, fmt, *args):
84 self.warn(fmt, *args)
86 def error(self, fmt, *args):
87 self._log(Log.ERROR, False, fmt, *args)
89 def fatal(self, fmt, *args):
90 self._log(Log.FATAL, False, fmt, *args)
92 def tracef(self, fmt, *args, **kwargs):
93 self._log(Log.TRACE, True, fmt, *args, **kwargs)
95 def debugf(self, fmt, *args, **kwargs):
96 self._log(Log.DEBUG, True, fmt, *args, **kwargs)
98 def infof(self, fmt, *args, **kwargs):
99 self._log(Log.INFO, True, fmt, *args, **kwargs)
101 def warnf(self, fmt, *args, **kwargs):
102 self._log(Log.WARN, True, fmt, *args, **kwargs)
104 def errorf(self, fmt, *args, **kwargs):
105 self._log(Log.ERROR, True, fmt, *args, **kwargs)
107 def fatalf(self, fmt, *args, **kwargs):
108 self._log(Log.FATAL, True, fmt, *args, **kwargs)
110 def _log(self, level, use_format, fmt, *args, **kwargs):
111 if self.isEnabledFor(level):
112 frame = inspect.currentframe().f_back # calling method
113 frame = frame.f_back # original log location
114 filename = os.path.split(frame.f_code.co_filename)[1]
115 funcname = frame.f_code.co_name
116 if use_format:
117 msg = fmt.format(*args, **kwargs) if args or kwargs else fmt
118 else:
119 msg = fmt % args if args else fmt
120 if self.UsePythonLogging:
121 pylog = logging.getLogger(self.getName())
122 record = logging.LogRecord(self.getName(), LevelTranslator.lsstLog2logging(level),
123 filename, frame.f_lineno, msg, None, False, func=funcname)
124 pylog.handle(record)
125 else:
126 self.logMsg(level, filename, funcname, frame.f_lineno, msg)
128 def __reduce__(self):
129 """Implement pickle support.
130 """
131 args = (self.getName(), )
132 # method has to be module-level, not class method
133 return (getLogger, args)
135# Export static functions from Log class to module namespace
138def configure(*args):
139 Log.configure(*args)
142def configure_prop(properties):
143 Log.configure_prop(properties)
146def getDefaultLogger():
147 return Log.getDefaultLogger()
150def getLogger(loggername):
151 return Log.getLogger(loggername)
154def MDC(key, value):
155 Log.MDC(key, str(value))
158def MDCRemove(key):
159 Log.MDCRemove(key)
162def MDCRegisterInit(func):
163 Log.MDCRegisterInit(func)
166def setLevel(loggername, level):
167 Log.getLogger(loggername).setLevel(level)
170def getLevel(loggername):
171 Log.getLogger(loggername).getLevel()
174def isEnabledFor(logger, level):
175 Log.getLogger(logger).isEnabledFor(level)
178def log(loggername, level, fmt, *args, **kwargs):
179 Log.getLogger(loggername)._log(level, False, fmt, *args)
182def trace(fmt, *args):
183 Log.getDefaultLogger()._log(TRACE, False, fmt, *args)
186def debug(fmt, *args):
187 Log.getDefaultLogger()._log(DEBUG, False, fmt, *args)
190def info(fmt, *args):
191 Log.getDefaultLogger()._log(INFO, False, fmt, *args)
194def warn(fmt, *args):
195 Log.getDefaultLogger()._log(WARN, False, fmt, *args)
198def warning(fmt, *args):
199 warn(fmt, *args)
202def error(fmt, *args):
203 Log.getDefaultLogger()._log(ERROR, False, fmt, *args)
206def fatal(fmt, *args):
207 Log.getDefaultLogger()._log(FATAL, False, fmt, *args)
210def logf(loggername, level, fmt, *args, **kwargs):
211 Log.getLogger(loggername)._log(level, True, fmt, *args, **kwargs)
214def tracef(fmt, *args, **kwargs):
215 Log.getDefaultLogger()._log(TRACE, True, fmt, *args, **kwargs)
218def debugf(fmt, *args, **kwargs):
219 Log.getDefaultLogger()._log(DEBUG, True, fmt, *args, **kwargs)
222def infof(fmt, *args, **kwargs):
223 Log.getDefaultLogger()._log(INFO, True, fmt, *args, **kwargs)
226def warnf(fmt, *args, **kwargs):
227 Log.getDefaultLogger()._log(WARN, True, fmt, *args, **kwargs)
230def errorf(fmt, *args, **kwargs):
231 Log.getDefaultLogger()._log(ERROR, True, fmt, *args, **kwargs)
234def fatalf(fmt, *args, **kwargs):
235 Log.getDefaultLogger()._log(FATAL, True, fmt, *args, **kwargs)
238def lwpID():
239 return Log.lwpID
242def usePythonLogging():
243 Log.usePythonLogging()
246def doNotUsePythonLogging():
247 Log.doNotUsePythonLogging()
250class UsePythonLogging:
251 """Context manager to enable Python log forwarding temporarily."""
253 def __init__(self):
254 self.current = Log.UsePythonLogging
256 def __enter__(self):
257 Log.usePythonLogging()
259 def __exit__(self, exc_type, exc_value, traceback):
260 Log.UsePythonLogging = self.current
263class LevelTranslator:
264 """Helper class to translate levels between ``lsst.log`` and Python
265 `logging`.
266 """
267 @staticmethod
268 def lsstLog2logging(level):
269 """Translates from lsst.log/log4cxx levels to `logging` module levels.
271 Parameters
272 ----------
273 level : `int`
274 Logging level number used by `lsst.log`, typically one of the
275 constants defined in this module (`DEBUG`, `INFO`, etc.)
277 Returns
278 -------
279 level : `int`
280 Correspoding logging level number for Python `logging` module.
281 """
282 # Python logging levels are same as lsst.log divided by 1000,
283 # logging does not have TRACE level by default but it is OK to use
284 # that numeric level and we may even add TRACE later.
285 return level//1000
287 @staticmethod
288 def logging2lsstLog(level):
289 """Translates from standard python `logging` module levels to
290 lsst.log/log4cxx levels.
292 Parameters
293 ----------
294 level : `int`
295 Logging level number used by Python `logging`, typically one of
296 the constants defined by `logging` module (`logging.DEBUG`,
297 `logging.INFO`, etc.)
299 Returns
300 -------
301 level : `int`
302 Correspoding logging level number for `lsst.log` module.
303 """
304 return level*1000
307class LogHandler(logging.Handler):
308 """Handler for Python logging module that emits to LSST logging.
310 Notes
311 -----
312 If this handler is enabled and `lsst.log` has been configured to use
313 Python `logging`, the handler will do nothing itself if any other
314 handler has been registered with the Python logger. If it does not
315 think that anything else is handling the message it will attempt to
316 send the message via a default `~logging.StreamHandler`. The safest
317 approach is to configure the logger with an additional handler
318 (possibly the ROOT logger) if `lsst.log` is to be configured to use
319 Python logging.
320 """
322 def __init__(self, level=logging.NOTSET):
323 logging.Handler.__init__(self, level=level)
324 # Format as a simple message because lsst.log will format the
325 # message a second time.
326 self.formatter = logging.Formatter(fmt="%(message)s")
328 def handle(self, record):
329 logger = Log.getLogger(record.name)
330 if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
331 logging.Handler.handle(self, record)
333 def emit(self, record):
334 if Log.UsePythonLogging:
335 # Do not forward this message to lsst.log since this may cause
336 # a logging loop.
338 # Work out whether any other handler is going to be invoked
339 # for this logger.
340 pylgr = logging.getLogger(record.name)
342 # If another handler is registered that is not LogHandler
343 # we ignore this request
344 if any(not isinstance(h, self.__class__) for h in pylgr.handlers):
345 return
347 # If the parent has handlers and propagation is enabled
348 # we punt as well (and if a LogHandler is involved then we will
349 # ask the same question when we get to it).
350 if pylgr.parent and pylgr.parent.hasHandlers() and pylgr.propagate:
351 return
353 # Force this message to appear somewhere.
354 # If something else should happen then the caller should add a
355 # second Handler.
356 stream = logging.StreamHandler()
357 stream.setFormatter(logging.Formatter(fmt="%(name)s %(levelname)s (fallback): %(message)s"))
358 stream.handle(record)
359 return
361 logger = Log.getLogger(record.name)
362 # Use standard formatting class to format message part of the record
363 message = self.formatter.format(record)
365 logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
366 record.filename, record.funcName,
367 record.lineno, message)