LSSTApplications  18.1.0
LSSTDataManagementBasePackage
logContinued.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
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 #
24 
25 import logging
26 import inspect
27 import os
28 
29 from lsst.utils import continueClass
30 
31 from .log import Log
32 
33 TRACE = 5000
34 DEBUG = 10000
35 INFO = 20000
36 WARN = 30000
37 ERROR = 40000
38 FATAL = 50000
39 
40 
41 @continueClass # noqa F811 redefinition
42 class Log:
43  UsePythonLogging = False
44  """Forward Python `lsst.log` messages to Python `logging` package."""
45 
46  @classmethod
47  def usePythonLogging(cls):
48  """Forward log messages to Python `logging`
49 
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.
55 
56  This state only affects messages sent to the `lsst.log`
57  package from Python.
58  """
59  cls.UsePythonLogging = True
60 
61  @classmethod
63  """Forward log messages to LSST logging system.
64 
65  Notes
66  -----
67  This is the default state.
68  """
69  cls.UsePythonLogging = False
70 
71  def trace(self, fmt, *args):
72  self._log(Log.TRACE, False, fmt, *args)
73 
74  def debug(self, fmt, *args):
75  self._log(Log.DEBUG, False, fmt, *args)
76 
77  def info(self, fmt, *args):
78  self._log(Log.INFO, False, fmt, *args)
79 
80  def warn(self, fmt, *args):
81  self._log(Log.WARN, False, fmt, *args)
82 
83  def warning(self, fmt, *args):
84  self.warn(fmt, *args)
85 
86  def error(self, fmt, *args):
87  self._log(Log.ERROR, False, fmt, *args)
88 
89  def fatal(self, fmt, *args):
90  self._log(Log.FATAL, False, fmt, *args)
91 
92  def tracef(self, fmt, *args, **kwargs):
93  self._log(Log.TRACE, True, fmt, *args, **kwargs)
94 
95  def debugf(self, fmt, *args, **kwargs):
96  self._log(Log.DEBUG, True, fmt, *args, **kwargs)
97 
98  def infof(self, fmt, *args, **kwargs):
99  self._log(Log.INFO, True, fmt, *args, **kwargs)
100 
101  def warnf(self, fmt, *args, **kwargs):
102  self._log(Log.WARN, True, fmt, *args, **kwargs)
103 
104  def errorf(self, fmt, *args, **kwargs):
105  self._log(Log.ERROR, True, fmt, *args, **kwargs)
106 
107  def fatalf(self, fmt, *args, **kwargs):
108  self._log(Log.FATAL, True, fmt, *args, **kwargs)
109 
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)
127 
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)
134 
135 # Export static functions from Log class to module namespace
136 
137 
138 def configure(*args):
139  Log.configure(*args)
140 
141 
142 def configure_prop(properties):
143  Log.configure_prop(properties)
144 
145 
147  return Log.getDefaultLogger()
148 
149 
150 def getLogger(loggername):
151  return Log.getLogger(loggername)
152 
153 
154 def MDC(key, value):
155  Log.MDC(key, str(value))
156 
157 
158 def MDCRemove(key):
159  Log.MDCRemove(key)
160 
161 
162 def MDCRegisterInit(func):
163  Log.MDCRegisterInit(func)
164 
165 
166 def setLevel(loggername, level):
167  Log.getLogger(loggername).setLevel(level)
168 
169 
170 def getLevel(loggername):
171  Log.getLogger(loggername).getLevel()
172 
173 
174 def isEnabledFor(logger, level):
175  Log.getLogger(logger).isEnabledFor(level)
176 
177 
178 def log(loggername, level, fmt, *args, **kwargs):
179  Log.getLogger(loggername)._log(level, False, fmt, *args)
180 
181 
182 def trace(fmt, *args):
183  Log.getDefaultLogger()._log(TRACE, False, fmt, *args)
184 
185 
186 def debug(fmt, *args):
187  Log.getDefaultLogger()._log(DEBUG, False, fmt, *args)
188 
189 
190 def info(fmt, *args):
191  Log.getDefaultLogger()._log(INFO, False, fmt, *args)
192 
193 
194 def warn(fmt, *args):
195  Log.getDefaultLogger()._log(WARN, False, fmt, *args)
196 
197 
198 def warning(fmt, *args):
199  warn(fmt, *args)
200 
201 
202 def error(fmt, *args):
203  Log.getDefaultLogger()._log(ERROR, False, fmt, *args)
204 
205 
206 def fatal(fmt, *args):
207  Log.getDefaultLogger()._log(FATAL, False, fmt, *args)
208 
209 
210 def logf(loggername, level, fmt, *args, **kwargs):
211  Log.getLogger(loggername)._log(level, True, fmt, *args, **kwargs)
212 
213 
214 def tracef(fmt, *args, **kwargs):
215  Log.getDefaultLogger()._log(TRACE, True, fmt, *args, **kwargs)
216 
217 
218 def debugf(fmt, *args, **kwargs):
219  Log.getDefaultLogger()._log(DEBUG, True, fmt, *args, **kwargs)
220 
221 
222 def infof(fmt, *args, **kwargs):
223  Log.getDefaultLogger()._log(INFO, True, fmt, *args, **kwargs)
224 
225 
226 def warnf(fmt, *args, **kwargs):
227  Log.getDefaultLogger()._log(WARN, True, fmt, *args, **kwargs)
228 
229 
230 def errorf(fmt, *args, **kwargs):
231  Log.getDefaultLogger()._log(ERROR, True, fmt, *args, **kwargs)
232 
233 
234 def fatalf(fmt, *args, **kwargs):
235  Log.getDefaultLogger()._log(FATAL, True, fmt, *args, **kwargs)
236 
237 
238 def lwpID():
239  return Log.lwpID
240 
241 
243  Log.usePythonLogging()
244 
245 
247  Log.doNotUsePythonLogging()
248 
249 
251  """Context manager to enable Python log forwarding temporarily."""
252 
253  def __init__(self):
254  self.current = Log.UsePythonLogging
255 
256  def __enter__(self):
257  Log.usePythonLogging()
258 
259  def __exit__(self, exc_type, exc_value, traceback):
260  Log.UsePythonLogging = self.current
261 
262 
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.
270 
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.)
276 
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
286 
287  @staticmethod
288  def logging2lsstLog(level):
289  """Translates from standard python `logging` module levels to
290  lsst.log/log4cxx levels.
291 
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.)
298 
299  Returns
300  -------
301  level : `int`
302  Correspoding logging level number for `lsst.log` module.
303  """
304  return level*1000
305 
306 
307 class LogHandler(logging.Handler):
308  """Handler for Python logging module that emits to LSST logging.
309 
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  """
321 
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")
327 
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)
332 
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.
337 
338  # Work out whether any other handler is going to be invoked
339  # for this logger.
340  pylgr = logging.getLogger(record.name)
341 
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
346 
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
352 
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
360 
361  logger = Log.getLogger(record.name)
362  # Use standard formatting class to format message part of the record
363  message = self.formatter.format(record)
364 
365  logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
366  record.filename, record.funcName,
367  record.lineno, message)
def debug(self, fmt, args)
Definition: logContinued.py:74
def trace(self, fmt, args)
Definition: logContinued.py:71
def infof(fmt, args, kwargs)
def errorf(fmt, args, kwargs)
def errorf(self, fmt, args, kwargs)
def __init__(self, level=logging.NOTSET)
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def fatalf(fmt, args, kwargs)
def debugf(self, fmt, args, kwargs)
Definition: logContinued.py:95
def _log(self, level, use_format, fmt, args, kwargs)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
def getLevel(loggername)
def __exit__(self, exc_type, exc_value, traceback)
def fatal(self, fmt, args)
Definition: logContinued.py:89
def tracef(self, fmt, args, kwargs)
Definition: logContinued.py:92
def warn(self, fmt, args)
Definition: logContinued.py:80
def setLevel(loggername, level)
def warnf(fmt, args, kwargs)
def infof(self, fmt, args, kwargs)
Definition: logContinued.py:98
def warnf(self, fmt, args, kwargs)
def fatalf(self, fmt, args, kwargs)
def log(loggername, level, fmt, args, kwargs)
def isEnabledFor(logger, level)
def logf(loggername, level, fmt, args, kwargs)
def error(self, fmt, args)
Definition: logContinued.py:86
def info(self, fmt, args)
Definition: logContinued.py:77
def tracef(fmt, args, kwargs)
def warning(self, fmt, args)
Definition: logContinued.py:83
def configure_prop(properties)
def debugf(fmt, args, kwargs)
def getLogger(loggername)