LSSTApplications  16.0-10-g0ee56ad,16.0-11-ga33d1f2+2,16.0-12-g3ef5c14+1,16.0-12-g71e5ef5+14,16.0-12-gbdf3636+2,16.0-13-g118c103+2,16.0-13-g8f68b0a,16.0-15-gbf5c1cb,16.0-16-gfd17674+2,16.0-17-g7c01f5c+2,16.0-18-g0a50484,16.0-20-ga20f992+3,16.0-21-g0e05fd4+2,16.0-21-g15e2d33,16.0-22-g62d8060,16.0-22-g847a80f,16.0-24-gfa57b64+1,16.0-28-g3990c221,16.0-3-gf928089+2,16.0-32-g88a4f23,16.0-34-gd7987ad,16.0-36-g6d07a18,16.0-4-g10fc685,16.0-4-g18f3627+24,16.0-4-g5f3a788+25,16.0-5-gaf5c3d7+1,16.0-5-gcc1f4bb,16.0-6-g3b92700,16.0-6-g4412fcd+2,16.0-6-g7235603+1,16.0-68-gb45e8e9d,16.0-7-g0913a87,16.0-7-g56728dd,16.0-8-g14ebd58,16.0-8-g2df868b,16.0-8-g4cec79c+1,16.0-81-g9604f5049,16.0-9-g5400cdc,16.0-9-ge6233d7+1,master-g2880f2d8cf+1,w.2019.05
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 
129 # Export static functions from Log class to module namespace
130 
131 
132 def configure(*args):
133  Log.configure(*args)
134 
135 
136 def configure_prop(properties):
137  Log.configure_prop(properties)
138 
139 
141  return Log.getDefaultLoggerName()
142 
143 
144 def pushContext(name):
145  Log.pushContext(name)
146 
147 
149  Log.popContext()
150 
151 
152 def MDC(key, value):
153  Log.MDC(key, str(value))
154 
155 
156 def MDCRemove(key):
157  Log.MDCRemove(key)
158 
159 
160 def MDCRegisterInit(func):
161  Log.MDCRegisterInit(func)
162 
163 
164 def setLevel(loggername, level):
165  Log.getLogger(loggername).setLevel(level)
166 
167 
168 def getLevel(loggername):
169  Log.getLogger(loggername).getLevel()
170 
171 
172 def isEnabledFor(logger, level):
173  Log.getLogger(logger).isEnabledFor(level)
174 
175 
176 def log(loggername, level, fmt, *args, **kwargs):
177  Log.getLogger(loggername)._log(level, False, fmt, *args)
178 
179 
180 def trace(fmt, *args):
181  Log.getDefaultLogger()._log(TRACE, False, fmt, *args)
182 
183 
184 def debug(fmt, *args):
185  Log.getDefaultLogger()._log(DEBUG, False, fmt, *args)
186 
187 
188 def info(fmt, *args):
189  Log.getDefaultLogger()._log(INFO, False, fmt, *args)
190 
191 
192 def warn(fmt, *args):
193  Log.getDefaultLogger()._log(WARN, False, fmt, *args)
194 
195 
196 def warning(fmt, *args):
197  warn(fmt, *args)
198 
199 
200 def error(fmt, *args):
201  Log.getDefaultLogger()._log(ERROR, False, fmt, *args)
202 
203 
204 def fatal(fmt, *args):
205  Log.getDefaultLogger()._log(FATAL, False, fmt, *args)
206 
207 
208 def logf(loggername, level, fmt, *args, **kwargs):
209  Log.getLogger(loggername)._log(level, True, fmt, *args, **kwargs)
210 
211 
212 def tracef(fmt, *args, **kwargs):
213  Log.getDefaultLogger()._log(TRACE, True, fmt, *args, **kwargs)
214 
215 
216 def debugf(fmt, *args, **kwargs):
217  Log.getDefaultLogger()._log(DEBUG, True, fmt, *args, **kwargs)
218 
219 
220 def infof(fmt, *args, **kwargs):
221  Log.getDefaultLogger()._log(INFO, True, fmt, *args, **kwargs)
222 
223 
224 def warnf(fmt, *args, **kwargs):
225  Log.getDefaultLogger()._log(WARN, True, fmt, *args, **kwargs)
226 
227 
228 def errorf(fmt, *args, **kwargs):
229  Log.getDefaultLogger()._log(ERROR, True, fmt, *args, **kwargs)
230 
231 
232 def fatalf(fmt, *args, **kwargs):
233  Log.getDefaultLogger()._log(FATAL, True, fmt, *args, **kwargs)
234 
235 
236 def lwpID():
237  return Log.lwpID
238 
239 
241  Log.usePythonLogging()
242 
243 
245  Log.doNotUsePythonLogging()
246 
247 
249  """Context manager for logging."""
250 
251  def __init__(self, name=None, level=None):
252  self.name = name
253  self.level = level
254 
255  def __enter__(self):
256  self.open()
257  return self
258 
259  def __exit__(self, type, value, traceback):
260  self.close()
261 
262  def __del__(self):
263  self.close()
264 
265  def open(self):
266  if self.name is not None:
267  Log.pushContext(self.name)
268  if self.level is not None:
269  Log.getDefaultLogger().setLevel(self.level)
270 
271  def close(self):
272  if self.name is not None:
273  Log.popContext()
274  self.name = None
275 
276  def setLevel(self, level):
277  Log.getDefaultLogger().setLevel(level)
278 
279  def getLevel(self):
280  return Log.getDefaultLogger().getLevel()
281 
282  def isEnabledFor(self, level):
283  return Log.getDefaultLogger().isEnabledFor(level)
284 
285 
287  """Context manager to enable Python log forwarding temporarily."""
288 
289  def __init__(self):
290  self.current = Log.UsePythonLogging
291 
292  def __enter__(self):
293  Log.usePythonLogging()
294 
295  def __exit__(self, exc_type, exc_value, traceback):
296  Log.UsePythonLogging = self.current
297 
298 
300  """Helper class to translate levels between ``lsst.log`` and Python
301  `logging`.
302  """
303  @staticmethod
304  def lsstLog2logging(level):
305  """Translates from lsst.log/log4cxx levels to `logging` module levels.
306 
307  Parameters
308  ----------
309  level : `int`
310  Logging level number used by `lsst.log`, typically one of the
311  constants defined in this module (`DEBUG`, `INFO`, etc.)
312 
313  Returns
314  -------
315  level : `int`
316  Correspoding logging level number for Python `logging` module.
317  """
318  # Python logging levels are same as lsst.log divided by 1000,
319  # logging does not have TRACE level by default but it is OK to use
320  # that numeric level and we may even add TRACE later.
321  return level//1000
322 
323  @staticmethod
324  def logging2lsstLog(level):
325  """Translates from standard python `logging` module levels to
326  lsst.log/log4cxx levels.
327 
328  Parameters
329  ----------
330  level : `int`
331  Logging level number used by Python `logging`, typically one of
332  the constants defined by `logging` module (`logging.DEBUG`,
333  `logging.INFO`, etc.)
334 
335  Returns
336  -------
337  level : `int`
338  Correspoding logging level number for `lsst.log` module.
339  """
340  return level*1000
341 
342 
343 class LogHandler(logging.Handler):
344  """Handler for Python logging module that emits to LSST logging.
345 
346  Notes
347  -----
348  If this handler is enabled and `lsst.log` has been configured to use
349  Python `logging`, the handler will do nothing itself if any other
350  handler has been registered with the Python logger. If it does not
351  think that anything else is handling the message it will attempt to
352  send the message via a default `~logging.StreamHandler`. The safest
353  approach is to configure the logger with an additional handler
354  (possibly the ROOT logger) if `lsst.log` is to be configured to use
355  Python logging.
356  """
357 
358  def __init__(self, level=logging.NOTSET):
359  logging.Handler.__init__(self, level=level)
360  # Format as a simple message because lsst.log will format the
361  # message a second time.
362  self.formatter = logging.Formatter(fmt="%(message)s")
363 
364  def handle(self, record):
365  logger = Log.getLogger(record.name)
366  if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
367  logging.Handler.handle(self, record)
368 
369  def emit(self, record):
370  if Log.UsePythonLogging:
371  # Do not forward this message to lsst.log since this may cause
372  # a logging loop.
373 
374  # Work out whether any other handler is going to be invoked
375  # for this logger.
376  pylgr = logging.getLogger(record.name)
377 
378  # If another handler is registered that is not LogHandler
379  # we ignore this request
380  if any(not isinstance(h, self.__class__) for h in pylgr.handlers):
381  return
382 
383  # If the parent has handlers and propagation is enabled
384  # we punt as well (and if a LogHandler is involved then we will
385  # ask the same question when we get to it).
386  if pylgr.parent and pylgr.parent.hasHandlers() and pylgr.propagate:
387  return
388 
389  # Force this message to appear somewhere.
390  # If something else should happen then the caller should add a
391  # second Handler.
392  stream = logging.StreamHandler()
393  stream.setFormatter(logging.Formatter(fmt="%(name)s %(levelname)s (fallback): %(message)s"))
394  stream.handle(record)
395  return
396 
397  logger = Log.getLogger(record.name)
398  # Use standard formatting class to format message part of the record
399  message = self.formatter.format(record)
400 
401  logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
402  record.filename, record.funcName,
403  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)
def __exit__(self, type, value, traceback)
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 __init__(self, name=None, level=None)
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)