LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
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 __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"]
32 
33 import logging
34 import inspect
35 import os
36 
37 from lsst.utils import continueClass
38 
39 from .log import Log
40 
41 TRACE = 5000
42 DEBUG = 10000
43 INFO = 20000
44 WARN = 30000
45 ERROR = 40000
46 FATAL = 50000
47 
48 
49 @continueClass # noqa: F811 (FIXME: remove for py 3.8+)
50 class Log: # noqa: F811
51  UsePythonLogging = False
52  """Forward Python `lsst.log` messages to Python `logging` package."""
53 
54  @classmethod
55  def usePythonLogging(cls):
56  """Forward log messages to Python `logging`
57 
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.
63 
64  This state only affects messages sent to the `lsst.log`
65  package from Python.
66  """
67  cls.UsePythonLoggingUsePythonLoggingUsePythonLogging = True
68 
69  @classmethod
71  """Forward log messages to LSST logging system.
72 
73  Notes
74  -----
75  This is the default state.
76  """
77  cls.UsePythonLoggingUsePythonLoggingUsePythonLogging = False
78 
79  def trace(self, fmt, *args):
80  self._log_log(Log.TRACE, False, fmt, *args)
81 
82  def debug(self, fmt, *args):
83  self._log_log(Log.DEBUG, False, fmt, *args)
84 
85  def info(self, fmt, *args):
86  self._log_log(Log.INFO, False, fmt, *args)
87 
88  def warn(self, fmt, *args):
89  self._log_log(Log.WARN, False, fmt, *args)
90 
91  def warning(self, fmt, *args):
92  self.warnwarn(fmt, *args)
93 
94  def error(self, fmt, *args):
95  self._log_log(Log.ERROR, False, fmt, *args)
96 
97  def fatal(self, fmt, *args):
98  self._log_log(Log.FATAL, False, fmt, *args)
99 
100  def tracef(self, fmt, *args, **kwargs):
101  self._log_log(Log.TRACE, True, fmt, *args, **kwargs)
102 
103  def debugf(self, fmt, *args, **kwargs):
104  self._log_log(Log.DEBUG, True, fmt, *args, **kwargs)
105 
106  def infof(self, fmt, *args, **kwargs):
107  self._log_log(Log.INFO, True, fmt, *args, **kwargs)
108 
109  def warnf(self, fmt, *args, **kwargs):
110  self._log_log(Log.WARN, True, fmt, *args, **kwargs)
111 
112  def errorf(self, fmt, *args, **kwargs):
113  self._log_log(Log.ERROR, True, fmt, *args, **kwargs)
114 
115  def fatalf(self, fmt, *args, **kwargs):
116  self._log_log(Log.FATAL, True, fmt, *args, **kwargs)
117 
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.UsePythonLoggingUsePythonLoggingUsePythonLogging:
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)
135 
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)
142 
143 # Export static functions from Log class to module namespace
144 
145 
146 def configure(*args):
147  Log.configure(*args)
148 
149 
150 def configure_prop(properties):
151  Log.configure_prop(properties)
152 
153 
155  return Log.getDefaultLogger()
156 
157 
158 def getLogger(loggername):
159  return Log.getLogger(loggername)
160 
161 
162 def MDC(key, value):
163  Log.MDC(key, str(value))
164 
165 
166 def MDCRemove(key):
167  Log.MDCRemove(key)
168 
169 
170 def MDCRegisterInit(func):
171  Log.MDCRegisterInit(func)
172 
173 
174 def setLevel(loggername, level):
175  Log.getLogger(loggername).setLevel(level)
176 
177 
178 def getLevel(loggername):
179  Log.getLogger(loggername).getLevel()
180 
181 
182 def isEnabledFor(logger, level):
183  Log.getLogger(logger).isEnabledFor(level)
184 
185 
186 # This will cause a warning in Sphinx documentation due to confusion between
187 # Log and log. https://github.com/astropy/sphinx-automodapi/issues/73 (but
188 # note that this does not seem to be Mac-only).
189 def log(loggername, level, fmt, *args, **kwargs):
190  Log.getLogger(loggername)._log(level, False, fmt, *args)
191 
192 
193 def trace(fmt, *args):
194  Log.getDefaultLogger()._log(TRACE, False, fmt, *args)
195 
196 
197 def debug(fmt, *args):
198  Log.getDefaultLogger()._log(DEBUG, False, fmt, *args)
199 
200 
201 def info(fmt, *args):
202  Log.getDefaultLogger()._log(INFO, False, fmt, *args)
203 
204 
205 def warn(fmt, *args):
206  Log.getDefaultLogger()._log(WARN, False, fmt, *args)
207 
208 
209 def warning(fmt, *args):
210  warn(fmt, *args)
211 
212 
213 def error(fmt, *args):
214  Log.getDefaultLogger()._log(ERROR, False, fmt, *args)
215 
216 
217 def fatal(fmt, *args):
218  Log.getDefaultLogger()._log(FATAL, False, fmt, *args)
219 
220 
221 def logf(loggername, level, fmt, *args, **kwargs):
222  Log.getLogger(loggername)._log(level, True, fmt, *args, **kwargs)
223 
224 
225 def tracef(fmt, *args, **kwargs):
226  Log.getDefaultLogger()._log(TRACE, True, fmt, *args, **kwargs)
227 
228 
229 def debugf(fmt, *args, **kwargs):
230  Log.getDefaultLogger()._log(DEBUG, True, fmt, *args, **kwargs)
231 
232 
233 def infof(fmt, *args, **kwargs):
234  Log.getDefaultLogger()._log(INFO, True, fmt, *args, **kwargs)
235 
236 
237 def warnf(fmt, *args, **kwargs):
238  Log.getDefaultLogger()._log(WARN, True, fmt, *args, **kwargs)
239 
240 
241 def errorf(fmt, *args, **kwargs):
242  Log.getDefaultLogger()._log(ERROR, True, fmt, *args, **kwargs)
243 
244 
245 def fatalf(fmt, *args, **kwargs):
246  Log.getDefaultLogger()._log(FATAL, True, fmt, *args, **kwargs)
247 
248 
249 def lwpID():
250  return Log.lwpID
251 
252 
253 # This will cause a warning in Sphinx documentation due to confusion between
254 # UsePythonLogging and usePythonLogging.
255 # https://github.com/astropy/sphinx-automodapi/issues/73 (but note that this
256 # does not seem to be Mac-only).
258  Log.usePythonLogging()
259 
260 
262  Log.doNotUsePythonLogging()
263 
264 
266  """Context manager to enable Python log forwarding temporarily.
267  """
268 
269  def __init__(self):
270  self.currentcurrent = Log.UsePythonLogging
271 
272  def __enter__(self):
273  Log.usePythonLogging()
274 
275  def __exit__(self, exc_type, exc_value, traceback):
276  Log.UsePythonLogging = self.currentcurrent
277 
278 
280  """Helper class to translate levels between ``lsst.log`` and Python
281  `logging`.
282  """
283  @staticmethod
284  def lsstLog2logging(level):
285  """Translates from lsst.log/log4cxx levels to `logging` module levels.
286 
287  Parameters
288  ----------
289  level : `int`
290  Logging level number used by `lsst.log`, typically one of the
291  constants defined in this module (`DEBUG`, `INFO`, etc.)
292 
293  Returns
294  -------
295  level : `int`
296  Correspoding logging level number for Python `logging` module.
297  """
298  # Python logging levels are same as lsst.log divided by 1000,
299  # logging does not have TRACE level by default but it is OK to use
300  # that numeric level and we may even add TRACE later.
301  return level//1000
302 
303  @staticmethod
304  def logging2lsstLog(level):
305  """Translates from standard python `logging` module levels to
306  lsst.log/log4cxx levels.
307 
308  Parameters
309  ----------
310  level : `int`
311  Logging level number used by Python `logging`, typically one of
312  the constants defined by `logging` module (`logging.DEBUG`,
313  `logging.INFO`, etc.)
314 
315  Returns
316  -------
317  level : `int`
318  Correspoding logging level number for `lsst.log` module.
319  """
320  return level*1000
321 
322 
323 class LogHandler(logging.Handler):
324  """Handler for Python logging module that emits to LSST logging.
325 
326  Parameters
327  ----------
328  level : `int`
329  Level at which to set the this handler.
330 
331  Notes
332  -----
333  If this handler is enabled and `lsst.log` has been configured to use
334  Python `logging`, the handler will do nothing itself if any other
335  handler has been registered with the Python logger. If it does not
336  think that anything else is handling the message it will attempt to
337  send the message via a default `~logging.StreamHandler`. The safest
338  approach is to configure the logger with an additional handler
339  (possibly the ROOT logger) if `lsst.log` is to be configured to use
340  Python logging.
341  """
342 
343  def __init__(self, level=logging.NOTSET):
344  logging.Handler.__init__(self, level=level)
345  # Format as a simple message because lsst.log will format the
346  # message a second time.
347  self.formatterformatter = logging.Formatter(fmt="%(message)s")
348 
349  def handle(self, record):
350  logger = Log.getLogger(record.name)
351  if logger.isEnabledFor(LevelTranslator.logging2lsstLog(record.levelno)):
352  logging.Handler.handle(self, record)
353 
354  def emit(self, record):
355  if Log.UsePythonLogging:
356  # Do not forward this message to lsst.log since this may cause
357  # a logging loop.
358 
359  # Work out whether any other handler is going to be invoked
360  # for this logger.
361  pylgr = logging.getLogger(record.name)
362 
363  # If another handler is registered that is not LogHandler
364  # we ignore this request
365  if any(not isinstance(h, self.__class__) for h in pylgr.handlers):
366  return
367 
368  # If the parent has handlers and propagation is enabled
369  # we punt as well (and if a LogHandler is involved then we will
370  # ask the same question when we get to it).
371  if pylgr.parent and pylgr.parent.hasHandlers() and pylgr.propagate:
372  return
373 
374  # Force this message to appear somewhere.
375  # If something else should happen then the caller should add a
376  # second Handler.
377  stream = logging.StreamHandler()
378  stream.setFormatter(logging.Formatter(fmt="%(name)s %(levelname)s (fallback): %(message)s"))
379  stream.handle(record)
380  return
381 
382  logger = Log.getLogger(record.name)
383  # Use standard formatting class to format message part of the record
384  message = self.formatterformatter.format(record)
385 
386  logger.logMsg(LevelTranslator.logging2lsstLog(record.levelno),
387  record.filename, record.funcName,
388  record.lineno, message)
def __init__(self, level=logging.NOTSET)
def fatal(self, fmt, *args)
Definition: logContinued.py:97
def trace(self, fmt, *args)
Definition: logContinued.py:79
def debugf(self, fmt, *args, **kwargs)
def warn(self, fmt, *args)
Definition: logContinued.py:88
def tracef(self, fmt, *args, **kwargs)
def error(self, fmt, *args)
Definition: logContinued.py:94
def warning(self, fmt, *args)
Definition: logContinued.py:91
def infof(self, fmt, *args, **kwargs)
def _log(self, level, use_format, fmt, *args, **kwargs)
def errorf(self, fmt, *args, **kwargs)
def debug(self, fmt, *args)
Definition: logContinued.py:82
def info(self, fmt, *args)
Definition: logContinued.py:85
def fatalf(self, fmt, *args, **kwargs)
def warnf(self, fmt, *args, **kwargs)
def __exit__(self, exc_type, exc_value, traceback)
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def setLevel(loggername, level)
def getLogger(loggername)
def fatalf(fmt, *args, **kwargs)
def tracef(fmt, *args, **kwargs)
def logf(loggername, level, fmt, *args, **kwargs)
def debugf(fmt, *args, **kwargs)
def infof(fmt, *args, **kwargs)
def log(loggername, level, fmt, *args, **kwargs)
def isEnabledFor(logger, level)
def warnf(fmt, *args, **kwargs)
def errorf(fmt, *args, **kwargs)
def getLevel(loggername)
def configure_prop(properties)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174