1 | n/a | # Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. |
---|
2 | n/a | # |
---|
3 | n/a | # Permission to use, copy, modify, and distribute this software and its |
---|
4 | n/a | # documentation for any purpose and without fee is hereby granted, |
---|
5 | n/a | # provided that the above copyright notice appear in all copies and that |
---|
6 | n/a | # both that copyright notice and this permission notice appear in |
---|
7 | n/a | # supporting documentation, and that the name of Vinay Sajip |
---|
8 | n/a | # not be used in advertising or publicity pertaining to distribution |
---|
9 | n/a | # of the software without specific, written prior permission. |
---|
10 | n/a | # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
---|
11 | n/a | # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL |
---|
12 | n/a | # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
---|
13 | n/a | # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
---|
14 | n/a | # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
---|
15 | n/a | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
16 | n/a | |
---|
17 | n/a | """ |
---|
18 | n/a | Logging package for Python. Based on PEP 282 and comments thereto in |
---|
19 | n/a | comp.lang.python. |
---|
20 | n/a | |
---|
21 | n/a | Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. |
---|
22 | n/a | |
---|
23 | n/a | To use, simply 'import logging' and log away! |
---|
24 | n/a | """ |
---|
25 | n/a | |
---|
26 | n/a | import sys, os, time, io, traceback, warnings, weakref, collections |
---|
27 | n/a | |
---|
28 | n/a | from string import Template |
---|
29 | n/a | |
---|
30 | n/a | __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR', |
---|
31 | n/a | 'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO', |
---|
32 | n/a | 'LogRecord', 'Logger', 'LoggerAdapter', 'NOTSET', 'NullHandler', |
---|
33 | n/a | 'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'basicConfig', |
---|
34 | n/a | 'captureWarnings', 'critical', 'debug', 'disable', 'error', |
---|
35 | n/a | 'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass', |
---|
36 | n/a | 'info', 'log', 'makeLogRecord', 'setLoggerClass', 'shutdown', |
---|
37 | n/a | 'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory', |
---|
38 | n/a | 'lastResort', 'raiseExceptions'] |
---|
39 | n/a | |
---|
40 | n/a | try: |
---|
41 | n/a | import threading |
---|
42 | n/a | except ImportError: #pragma: no cover |
---|
43 | n/a | threading = None |
---|
44 | n/a | |
---|
45 | n/a | __author__ = "Vinay Sajip <vinay_sajip@red-dove.com>" |
---|
46 | n/a | __status__ = "production" |
---|
47 | n/a | # The following module attributes are no longer updated. |
---|
48 | n/a | __version__ = "0.5.1.2" |
---|
49 | n/a | __date__ = "07 February 2010" |
---|
50 | n/a | |
---|
51 | n/a | #--------------------------------------------------------------------------- |
---|
52 | n/a | # Miscellaneous module data |
---|
53 | n/a | #--------------------------------------------------------------------------- |
---|
54 | n/a | |
---|
55 | n/a | # |
---|
56 | n/a | #_startTime is used as the base when calculating the relative time of events |
---|
57 | n/a | # |
---|
58 | n/a | _startTime = time.time() |
---|
59 | n/a | |
---|
60 | n/a | # |
---|
61 | n/a | #raiseExceptions is used to see if exceptions during handling should be |
---|
62 | n/a | #propagated |
---|
63 | n/a | # |
---|
64 | n/a | raiseExceptions = True |
---|
65 | n/a | |
---|
66 | n/a | # |
---|
67 | n/a | # If you don't want threading information in the log, set this to zero |
---|
68 | n/a | # |
---|
69 | n/a | logThreads = True |
---|
70 | n/a | |
---|
71 | n/a | # |
---|
72 | n/a | # If you don't want multiprocessing information in the log, set this to zero |
---|
73 | n/a | # |
---|
74 | n/a | logMultiprocessing = True |
---|
75 | n/a | |
---|
76 | n/a | # |
---|
77 | n/a | # If you don't want process information in the log, set this to zero |
---|
78 | n/a | # |
---|
79 | n/a | logProcesses = True |
---|
80 | n/a | |
---|
81 | n/a | #--------------------------------------------------------------------------- |
---|
82 | n/a | # Level related stuff |
---|
83 | n/a | #--------------------------------------------------------------------------- |
---|
84 | n/a | # |
---|
85 | n/a | # Default levels and level names, these can be replaced with any positive set |
---|
86 | n/a | # of values having corresponding names. There is a pseudo-level, NOTSET, which |
---|
87 | n/a | # is only really there as a lower limit for user-defined levels. Handlers and |
---|
88 | n/a | # loggers are initialized with NOTSET so that they will log all messages, even |
---|
89 | n/a | # at user-defined levels. |
---|
90 | n/a | # |
---|
91 | n/a | |
---|
92 | n/a | CRITICAL = 50 |
---|
93 | n/a | FATAL = CRITICAL |
---|
94 | n/a | ERROR = 40 |
---|
95 | n/a | WARNING = 30 |
---|
96 | n/a | WARN = WARNING |
---|
97 | n/a | INFO = 20 |
---|
98 | n/a | DEBUG = 10 |
---|
99 | n/a | NOTSET = 0 |
---|
100 | n/a | |
---|
101 | n/a | _levelToName = { |
---|
102 | n/a | CRITICAL: 'CRITICAL', |
---|
103 | n/a | ERROR: 'ERROR', |
---|
104 | n/a | WARNING: 'WARNING', |
---|
105 | n/a | INFO: 'INFO', |
---|
106 | n/a | DEBUG: 'DEBUG', |
---|
107 | n/a | NOTSET: 'NOTSET', |
---|
108 | n/a | } |
---|
109 | n/a | _nameToLevel = { |
---|
110 | n/a | 'CRITICAL': CRITICAL, |
---|
111 | n/a | 'FATAL': FATAL, |
---|
112 | n/a | 'ERROR': ERROR, |
---|
113 | n/a | 'WARN': WARNING, |
---|
114 | n/a | 'WARNING': WARNING, |
---|
115 | n/a | 'INFO': INFO, |
---|
116 | n/a | 'DEBUG': DEBUG, |
---|
117 | n/a | 'NOTSET': NOTSET, |
---|
118 | n/a | } |
---|
119 | n/a | |
---|
120 | n/a | def getLevelName(level): |
---|
121 | n/a | """ |
---|
122 | n/a | Return the textual representation of logging level 'level'. |
---|
123 | n/a | |
---|
124 | n/a | If the level is one of the predefined levels (CRITICAL, ERROR, WARNING, |
---|
125 | n/a | INFO, DEBUG) then you get the corresponding string. If you have |
---|
126 | n/a | associated levels with names using addLevelName then the name you have |
---|
127 | n/a | associated with 'level' is returned. |
---|
128 | n/a | |
---|
129 | n/a | If a numeric value corresponding to one of the defined levels is passed |
---|
130 | n/a | in, the corresponding string representation is returned. |
---|
131 | n/a | |
---|
132 | n/a | Otherwise, the string "Level %s" % level is returned. |
---|
133 | n/a | """ |
---|
134 | n/a | # See Issues #22386, #27937 and #29220 for why it's this way |
---|
135 | n/a | result = _levelToName.get(level) |
---|
136 | n/a | if result is not None: |
---|
137 | n/a | return result |
---|
138 | n/a | result = _nameToLevel.get(level) |
---|
139 | n/a | if result is not None: |
---|
140 | n/a | return result |
---|
141 | n/a | return "Level %s" % level |
---|
142 | n/a | |
---|
143 | n/a | def addLevelName(level, levelName): |
---|
144 | n/a | """ |
---|
145 | n/a | Associate 'levelName' with 'level'. |
---|
146 | n/a | |
---|
147 | n/a | This is used when converting levels to text during message formatting. |
---|
148 | n/a | """ |
---|
149 | n/a | _acquireLock() |
---|
150 | n/a | try: #unlikely to cause an exception, but you never know... |
---|
151 | n/a | _levelToName[level] = levelName |
---|
152 | n/a | _nameToLevel[levelName] = level |
---|
153 | n/a | finally: |
---|
154 | n/a | _releaseLock() |
---|
155 | n/a | |
---|
156 | n/a | if hasattr(sys, '_getframe'): |
---|
157 | n/a | currentframe = lambda: sys._getframe(3) |
---|
158 | n/a | else: #pragma: no cover |
---|
159 | n/a | def currentframe(): |
---|
160 | n/a | """Return the frame object for the caller's stack frame.""" |
---|
161 | n/a | try: |
---|
162 | n/a | raise Exception |
---|
163 | n/a | except Exception: |
---|
164 | n/a | return sys.exc_info()[2].tb_frame.f_back |
---|
165 | n/a | |
---|
166 | n/a | # |
---|
167 | n/a | # _srcfile is used when walking the stack to check when we've got the first |
---|
168 | n/a | # caller stack frame, by skipping frames whose filename is that of this |
---|
169 | n/a | # module's source. It therefore should contain the filename of this module's |
---|
170 | n/a | # source file. |
---|
171 | n/a | # |
---|
172 | n/a | # Ordinarily we would use __file__ for this, but frozen modules don't always |
---|
173 | n/a | # have __file__ set, for some reason (see Issue #21736). Thus, we get the |
---|
174 | n/a | # filename from a handy code object from a function defined in this module. |
---|
175 | n/a | # (There's no particular reason for picking addLevelName.) |
---|
176 | n/a | # |
---|
177 | n/a | |
---|
178 | n/a | _srcfile = os.path.normcase(addLevelName.__code__.co_filename) |
---|
179 | n/a | |
---|
180 | n/a | # _srcfile is only used in conjunction with sys._getframe(). |
---|
181 | n/a | # To provide compatibility with older versions of Python, set _srcfile |
---|
182 | n/a | # to None if _getframe() is not available; this value will prevent |
---|
183 | n/a | # findCaller() from being called. You can also do this if you want to avoid |
---|
184 | n/a | # the overhead of fetching caller information, even when _getframe() is |
---|
185 | n/a | # available. |
---|
186 | n/a | #if not hasattr(sys, '_getframe'): |
---|
187 | n/a | # _srcfile = None |
---|
188 | n/a | |
---|
189 | n/a | |
---|
190 | n/a | def _checkLevel(level): |
---|
191 | n/a | if isinstance(level, int): |
---|
192 | n/a | rv = level |
---|
193 | n/a | elif str(level) == level: |
---|
194 | n/a | if level not in _nameToLevel: |
---|
195 | n/a | raise ValueError("Unknown level: %r" % level) |
---|
196 | n/a | rv = _nameToLevel[level] |
---|
197 | n/a | else: |
---|
198 | n/a | raise TypeError("Level not an integer or a valid string: %r" % level) |
---|
199 | n/a | return rv |
---|
200 | n/a | |
---|
201 | n/a | #--------------------------------------------------------------------------- |
---|
202 | n/a | # Thread-related stuff |
---|
203 | n/a | #--------------------------------------------------------------------------- |
---|
204 | n/a | |
---|
205 | n/a | # |
---|
206 | n/a | #_lock is used to serialize access to shared data structures in this module. |
---|
207 | n/a | #This needs to be an RLock because fileConfig() creates and configures |
---|
208 | n/a | #Handlers, and so might arbitrary user threads. Since Handler code updates the |
---|
209 | n/a | #shared dictionary _handlers, it needs to acquire the lock. But if configuring, |
---|
210 | n/a | #the lock would already have been acquired - so we need an RLock. |
---|
211 | n/a | #The same argument applies to Loggers and Manager.loggerDict. |
---|
212 | n/a | # |
---|
213 | n/a | if threading: |
---|
214 | n/a | _lock = threading.RLock() |
---|
215 | n/a | else: #pragma: no cover |
---|
216 | n/a | _lock = None |
---|
217 | n/a | |
---|
218 | n/a | |
---|
219 | n/a | def _acquireLock(): |
---|
220 | n/a | """ |
---|
221 | n/a | Acquire the module-level lock for serializing access to shared data. |
---|
222 | n/a | |
---|
223 | n/a | This should be released with _releaseLock(). |
---|
224 | n/a | """ |
---|
225 | n/a | if _lock: |
---|
226 | n/a | _lock.acquire() |
---|
227 | n/a | |
---|
228 | n/a | def _releaseLock(): |
---|
229 | n/a | """ |
---|
230 | n/a | Release the module-level lock acquired by calling _acquireLock(). |
---|
231 | n/a | """ |
---|
232 | n/a | if _lock: |
---|
233 | n/a | _lock.release() |
---|
234 | n/a | |
---|
235 | n/a | #--------------------------------------------------------------------------- |
---|
236 | n/a | # The logging record |
---|
237 | n/a | #--------------------------------------------------------------------------- |
---|
238 | n/a | |
---|
239 | n/a | class LogRecord(object): |
---|
240 | n/a | """ |
---|
241 | n/a | A LogRecord instance represents an event being logged. |
---|
242 | n/a | |
---|
243 | n/a | LogRecord instances are created every time something is logged. They |
---|
244 | n/a | contain all the information pertinent to the event being logged. The |
---|
245 | n/a | main information passed in is in msg and args, which are combined |
---|
246 | n/a | using str(msg) % args to create the message field of the record. The |
---|
247 | n/a | record also includes information such as when the record was created, |
---|
248 | n/a | the source line where the logging call was made, and any exception |
---|
249 | n/a | information to be logged. |
---|
250 | n/a | """ |
---|
251 | n/a | def __init__(self, name, level, pathname, lineno, |
---|
252 | n/a | msg, args, exc_info, func=None, sinfo=None, **kwargs): |
---|
253 | n/a | """ |
---|
254 | n/a | Initialize a logging record with interesting information. |
---|
255 | n/a | """ |
---|
256 | n/a | ct = time.time() |
---|
257 | n/a | self.name = name |
---|
258 | n/a | self.msg = msg |
---|
259 | n/a | # |
---|
260 | n/a | # The following statement allows passing of a dictionary as a sole |
---|
261 | n/a | # argument, so that you can do something like |
---|
262 | n/a | # logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2}) |
---|
263 | n/a | # Suggested by Stefan Behnel. |
---|
264 | n/a | # Note that without the test for args[0], we get a problem because |
---|
265 | n/a | # during formatting, we test to see if the arg is present using |
---|
266 | n/a | # 'if self.args:'. If the event being logged is e.g. 'Value is %d' |
---|
267 | n/a | # and if the passed arg fails 'if self.args:' then no formatting |
---|
268 | n/a | # is done. For example, logger.warning('Value is %d', 0) would log |
---|
269 | n/a | # 'Value is %d' instead of 'Value is 0'. |
---|
270 | n/a | # For the use case of passing a dictionary, this should not be a |
---|
271 | n/a | # problem. |
---|
272 | n/a | # Issue #21172: a request was made to relax the isinstance check |
---|
273 | n/a | # to hasattr(args[0], '__getitem__'). However, the docs on string |
---|
274 | n/a | # formatting still seem to suggest a mapping object is required. |
---|
275 | n/a | # Thus, while not removing the isinstance check, it does now look |
---|
276 | n/a | # for collections.Mapping rather than, as before, dict. |
---|
277 | n/a | if (args and len(args) == 1 and isinstance(args[0], collections.Mapping) |
---|
278 | n/a | and args[0]): |
---|
279 | n/a | args = args[0] |
---|
280 | n/a | self.args = args |
---|
281 | n/a | self.levelname = getLevelName(level) |
---|
282 | n/a | self.levelno = level |
---|
283 | n/a | self.pathname = pathname |
---|
284 | n/a | try: |
---|
285 | n/a | self.filename = os.path.basename(pathname) |
---|
286 | n/a | self.module = os.path.splitext(self.filename)[0] |
---|
287 | n/a | except (TypeError, ValueError, AttributeError): |
---|
288 | n/a | self.filename = pathname |
---|
289 | n/a | self.module = "Unknown module" |
---|
290 | n/a | self.exc_info = exc_info |
---|
291 | n/a | self.exc_text = None # used to cache the traceback text |
---|
292 | n/a | self.stack_info = sinfo |
---|
293 | n/a | self.lineno = lineno |
---|
294 | n/a | self.funcName = func |
---|
295 | n/a | self.created = ct |
---|
296 | n/a | self.msecs = (ct - int(ct)) * 1000 |
---|
297 | n/a | self.relativeCreated = (self.created - _startTime) * 1000 |
---|
298 | n/a | if logThreads and threading: |
---|
299 | n/a | self.thread = threading.get_ident() |
---|
300 | n/a | self.threadName = threading.current_thread().name |
---|
301 | n/a | else: # pragma: no cover |
---|
302 | n/a | self.thread = None |
---|
303 | n/a | self.threadName = None |
---|
304 | n/a | if not logMultiprocessing: # pragma: no cover |
---|
305 | n/a | self.processName = None |
---|
306 | n/a | else: |
---|
307 | n/a | self.processName = 'MainProcess' |
---|
308 | n/a | mp = sys.modules.get('multiprocessing') |
---|
309 | n/a | if mp is not None: |
---|
310 | n/a | # Errors may occur if multiprocessing has not finished loading |
---|
311 | n/a | # yet - e.g. if a custom import hook causes third-party code |
---|
312 | n/a | # to run when multiprocessing calls import. See issue 8200 |
---|
313 | n/a | # for an example |
---|
314 | n/a | try: |
---|
315 | n/a | self.processName = mp.current_process().name |
---|
316 | n/a | except Exception: #pragma: no cover |
---|
317 | n/a | pass |
---|
318 | n/a | if logProcesses and hasattr(os, 'getpid'): |
---|
319 | n/a | self.process = os.getpid() |
---|
320 | n/a | else: |
---|
321 | n/a | self.process = None |
---|
322 | n/a | |
---|
323 | n/a | def __str__(self): |
---|
324 | n/a | return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno, |
---|
325 | n/a | self.pathname, self.lineno, self.msg) |
---|
326 | n/a | |
---|
327 | n/a | __repr__ = __str__ |
---|
328 | n/a | |
---|
329 | n/a | def getMessage(self): |
---|
330 | n/a | """ |
---|
331 | n/a | Return the message for this LogRecord. |
---|
332 | n/a | |
---|
333 | n/a | Return the message for this LogRecord after merging any user-supplied |
---|
334 | n/a | arguments with the message. |
---|
335 | n/a | """ |
---|
336 | n/a | msg = str(self.msg) |
---|
337 | n/a | if self.args: |
---|
338 | n/a | msg = msg % self.args |
---|
339 | n/a | return msg |
---|
340 | n/a | |
---|
341 | n/a | # |
---|
342 | n/a | # Determine which class to use when instantiating log records. |
---|
343 | n/a | # |
---|
344 | n/a | _logRecordFactory = LogRecord |
---|
345 | n/a | |
---|
346 | n/a | def setLogRecordFactory(factory): |
---|
347 | n/a | """ |
---|
348 | n/a | Set the factory to be used when instantiating a log record. |
---|
349 | n/a | |
---|
350 | n/a | :param factory: A callable which will be called to instantiate |
---|
351 | n/a | a log record. |
---|
352 | n/a | """ |
---|
353 | n/a | global _logRecordFactory |
---|
354 | n/a | _logRecordFactory = factory |
---|
355 | n/a | |
---|
356 | n/a | def getLogRecordFactory(): |
---|
357 | n/a | """ |
---|
358 | n/a | Return the factory to be used when instantiating a log record. |
---|
359 | n/a | """ |
---|
360 | n/a | |
---|
361 | n/a | return _logRecordFactory |
---|
362 | n/a | |
---|
363 | n/a | def makeLogRecord(dict): |
---|
364 | n/a | """ |
---|
365 | n/a | Make a LogRecord whose attributes are defined by the specified dictionary, |
---|
366 | n/a | This function is useful for converting a logging event received over |
---|
367 | n/a | a socket connection (which is sent as a dictionary) into a LogRecord |
---|
368 | n/a | instance. |
---|
369 | n/a | """ |
---|
370 | n/a | rv = _logRecordFactory(None, None, "", 0, "", (), None, None) |
---|
371 | n/a | rv.__dict__.update(dict) |
---|
372 | n/a | return rv |
---|
373 | n/a | |
---|
374 | n/a | #--------------------------------------------------------------------------- |
---|
375 | n/a | # Formatter classes and functions |
---|
376 | n/a | #--------------------------------------------------------------------------- |
---|
377 | n/a | |
---|
378 | n/a | class PercentStyle(object): |
---|
379 | n/a | |
---|
380 | n/a | default_format = '%(message)s' |
---|
381 | n/a | asctime_format = '%(asctime)s' |
---|
382 | n/a | asctime_search = '%(asctime)' |
---|
383 | n/a | |
---|
384 | n/a | def __init__(self, fmt): |
---|
385 | n/a | self._fmt = fmt or self.default_format |
---|
386 | n/a | |
---|
387 | n/a | def usesTime(self): |
---|
388 | n/a | return self._fmt.find(self.asctime_search) >= 0 |
---|
389 | n/a | |
---|
390 | n/a | def format(self, record): |
---|
391 | n/a | return self._fmt % record.__dict__ |
---|
392 | n/a | |
---|
393 | n/a | class StrFormatStyle(PercentStyle): |
---|
394 | n/a | default_format = '{message}' |
---|
395 | n/a | asctime_format = '{asctime}' |
---|
396 | n/a | asctime_search = '{asctime' |
---|
397 | n/a | |
---|
398 | n/a | def format(self, record): |
---|
399 | n/a | return self._fmt.format(**record.__dict__) |
---|
400 | n/a | |
---|
401 | n/a | |
---|
402 | n/a | class StringTemplateStyle(PercentStyle): |
---|
403 | n/a | default_format = '${message}' |
---|
404 | n/a | asctime_format = '${asctime}' |
---|
405 | n/a | asctime_search = '${asctime}' |
---|
406 | n/a | |
---|
407 | n/a | def __init__(self, fmt): |
---|
408 | n/a | self._fmt = fmt or self.default_format |
---|
409 | n/a | self._tpl = Template(self._fmt) |
---|
410 | n/a | |
---|
411 | n/a | def usesTime(self): |
---|
412 | n/a | fmt = self._fmt |
---|
413 | n/a | return fmt.find('$asctime') >= 0 or fmt.find(self.asctime_format) >= 0 |
---|
414 | n/a | |
---|
415 | n/a | def format(self, record): |
---|
416 | n/a | return self._tpl.substitute(**record.__dict__) |
---|
417 | n/a | |
---|
418 | n/a | BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" |
---|
419 | n/a | |
---|
420 | n/a | _STYLES = { |
---|
421 | n/a | '%': (PercentStyle, BASIC_FORMAT), |
---|
422 | n/a | '{': (StrFormatStyle, '{levelname}:{name}:{message}'), |
---|
423 | n/a | '$': (StringTemplateStyle, '${levelname}:${name}:${message}'), |
---|
424 | n/a | } |
---|
425 | n/a | |
---|
426 | n/a | class Formatter(object): |
---|
427 | n/a | """ |
---|
428 | n/a | Formatter instances are used to convert a LogRecord to text. |
---|
429 | n/a | |
---|
430 | n/a | Formatters need to know how a LogRecord is constructed. They are |
---|
431 | n/a | responsible for converting a LogRecord to (usually) a string which can |
---|
432 | n/a | be interpreted by either a human or an external system. The base Formatter |
---|
433 | n/a | allows a formatting string to be specified. If none is supplied, the |
---|
434 | n/a | default value of "%s(message)" is used. |
---|
435 | n/a | |
---|
436 | n/a | The Formatter can be initialized with a format string which makes use of |
---|
437 | n/a | knowledge of the LogRecord attributes - e.g. the default value mentioned |
---|
438 | n/a | above makes use of the fact that the user's message and arguments are pre- |
---|
439 | n/a | formatted into a LogRecord's message attribute. Currently, the useful |
---|
440 | n/a | attributes in a LogRecord are described by: |
---|
441 | n/a | |
---|
442 | n/a | %(name)s Name of the logger (logging channel) |
---|
443 | n/a | %(levelno)s Numeric logging level for the message (DEBUG, INFO, |
---|
444 | n/a | WARNING, ERROR, CRITICAL) |
---|
445 | n/a | %(levelname)s Text logging level for the message ("DEBUG", "INFO", |
---|
446 | n/a | "WARNING", "ERROR", "CRITICAL") |
---|
447 | n/a | %(pathname)s Full pathname of the source file where the logging |
---|
448 | n/a | call was issued (if available) |
---|
449 | n/a | %(filename)s Filename portion of pathname |
---|
450 | n/a | %(module)s Module (name portion of filename) |
---|
451 | n/a | %(lineno)d Source line number where the logging call was issued |
---|
452 | n/a | (if available) |
---|
453 | n/a | %(funcName)s Function name |
---|
454 | n/a | %(created)f Time when the LogRecord was created (time.time() |
---|
455 | n/a | return value) |
---|
456 | n/a | %(asctime)s Textual time when the LogRecord was created |
---|
457 | n/a | %(msecs)d Millisecond portion of the creation time |
---|
458 | n/a | %(relativeCreated)d Time in milliseconds when the LogRecord was created, |
---|
459 | n/a | relative to the time the logging module was loaded |
---|
460 | n/a | (typically at application startup time) |
---|
461 | n/a | %(thread)d Thread ID (if available) |
---|
462 | n/a | %(threadName)s Thread name (if available) |
---|
463 | n/a | %(process)d Process ID (if available) |
---|
464 | n/a | %(message)s The result of record.getMessage(), computed just as |
---|
465 | n/a | the record is emitted |
---|
466 | n/a | """ |
---|
467 | n/a | |
---|
468 | n/a | converter = time.localtime |
---|
469 | n/a | |
---|
470 | n/a | def __init__(self, fmt=None, datefmt=None, style='%'): |
---|
471 | n/a | """ |
---|
472 | n/a | Initialize the formatter with specified format strings. |
---|
473 | n/a | |
---|
474 | n/a | Initialize the formatter either with the specified format string, or a |
---|
475 | n/a | default as described above. Allow for specialized date formatting with |
---|
476 | n/a | the optional datefmt argument (if omitted, you get the ISO8601 format). |
---|
477 | n/a | |
---|
478 | n/a | Use a style parameter of '%', '{' or '$' to specify that you want to |
---|
479 | n/a | use one of %-formatting, :meth:`str.format` (``{}``) formatting or |
---|
480 | n/a | :class:`string.Template` formatting in your format string. |
---|
481 | n/a | |
---|
482 | n/a | .. versionchanged:: 3.2 |
---|
483 | n/a | Added the ``style`` parameter. |
---|
484 | n/a | """ |
---|
485 | n/a | if style not in _STYLES: |
---|
486 | n/a | raise ValueError('Style must be one of: %s' % ','.join( |
---|
487 | n/a | _STYLES.keys())) |
---|
488 | n/a | self._style = _STYLES[style][0](fmt) |
---|
489 | n/a | self._fmt = self._style._fmt |
---|
490 | n/a | self.datefmt = datefmt |
---|
491 | n/a | |
---|
492 | n/a | default_time_format = '%Y-%m-%d %H:%M:%S' |
---|
493 | n/a | default_msec_format = '%s,%03d' |
---|
494 | n/a | |
---|
495 | n/a | def formatTime(self, record, datefmt=None): |
---|
496 | n/a | """ |
---|
497 | n/a | Return the creation time of the specified LogRecord as formatted text. |
---|
498 | n/a | |
---|
499 | n/a | This method should be called from format() by a formatter which |
---|
500 | n/a | wants to make use of a formatted time. This method can be overridden |
---|
501 | n/a | in formatters to provide for any specific requirement, but the |
---|
502 | n/a | basic behaviour is as follows: if datefmt (a string) is specified, |
---|
503 | n/a | it is used with time.strftime() to format the creation time of the |
---|
504 | n/a | record. Otherwise, the ISO8601 format is used. The resulting |
---|
505 | n/a | string is returned. This function uses a user-configurable function |
---|
506 | n/a | to convert the creation time to a tuple. By default, time.localtime() |
---|
507 | n/a | is used; to change this for a particular formatter instance, set the |
---|
508 | n/a | 'converter' attribute to a function with the same signature as |
---|
509 | n/a | time.localtime() or time.gmtime(). To change it for all formatters, |
---|
510 | n/a | for example if you want all logging times to be shown in GMT, |
---|
511 | n/a | set the 'converter' attribute in the Formatter class. |
---|
512 | n/a | """ |
---|
513 | n/a | ct = self.converter(record.created) |
---|
514 | n/a | if datefmt: |
---|
515 | n/a | s = time.strftime(datefmt, ct) |
---|
516 | n/a | else: |
---|
517 | n/a | t = time.strftime(self.default_time_format, ct) |
---|
518 | n/a | s = self.default_msec_format % (t, record.msecs) |
---|
519 | n/a | return s |
---|
520 | n/a | |
---|
521 | n/a | def formatException(self, ei): |
---|
522 | n/a | """ |
---|
523 | n/a | Format and return the specified exception information as a string. |
---|
524 | n/a | |
---|
525 | n/a | This default implementation just uses |
---|
526 | n/a | traceback.print_exception() |
---|
527 | n/a | """ |
---|
528 | n/a | sio = io.StringIO() |
---|
529 | n/a | tb = ei[2] |
---|
530 | n/a | # See issues #9427, #1553375. Commented out for now. |
---|
531 | n/a | #if getattr(self, 'fullstack', False): |
---|
532 | n/a | # traceback.print_stack(tb.tb_frame.f_back, file=sio) |
---|
533 | n/a | traceback.print_exception(ei[0], ei[1], tb, None, sio) |
---|
534 | n/a | s = sio.getvalue() |
---|
535 | n/a | sio.close() |
---|
536 | n/a | if s[-1:] == "\n": |
---|
537 | n/a | s = s[:-1] |
---|
538 | n/a | return s |
---|
539 | n/a | |
---|
540 | n/a | def usesTime(self): |
---|
541 | n/a | """ |
---|
542 | n/a | Check if the format uses the creation time of the record. |
---|
543 | n/a | """ |
---|
544 | n/a | return self._style.usesTime() |
---|
545 | n/a | |
---|
546 | n/a | def formatMessage(self, record): |
---|
547 | n/a | return self._style.format(record) |
---|
548 | n/a | |
---|
549 | n/a | def formatStack(self, stack_info): |
---|
550 | n/a | """ |
---|
551 | n/a | This method is provided as an extension point for specialized |
---|
552 | n/a | formatting of stack information. |
---|
553 | n/a | |
---|
554 | n/a | The input data is a string as returned from a call to |
---|
555 | n/a | :func:`traceback.print_stack`, but with the last trailing newline |
---|
556 | n/a | removed. |
---|
557 | n/a | |
---|
558 | n/a | The base implementation just returns the value passed in. |
---|
559 | n/a | """ |
---|
560 | n/a | return stack_info |
---|
561 | n/a | |
---|
562 | n/a | def format(self, record): |
---|
563 | n/a | """ |
---|
564 | n/a | Format the specified record as text. |
---|
565 | n/a | |
---|
566 | n/a | The record's attribute dictionary is used as the operand to a |
---|
567 | n/a | string formatting operation which yields the returned string. |
---|
568 | n/a | Before formatting the dictionary, a couple of preparatory steps |
---|
569 | n/a | are carried out. The message attribute of the record is computed |
---|
570 | n/a | using LogRecord.getMessage(). If the formatting string uses the |
---|
571 | n/a | time (as determined by a call to usesTime(), formatTime() is |
---|
572 | n/a | called to format the event time. If there is exception information, |
---|
573 | n/a | it is formatted using formatException() and appended to the message. |
---|
574 | n/a | """ |
---|
575 | n/a | record.message = record.getMessage() |
---|
576 | n/a | if self.usesTime(): |
---|
577 | n/a | record.asctime = self.formatTime(record, self.datefmt) |
---|
578 | n/a | s = self.formatMessage(record) |
---|
579 | n/a | if record.exc_info: |
---|
580 | n/a | # Cache the traceback text to avoid converting it multiple times |
---|
581 | n/a | # (it's constant anyway) |
---|
582 | n/a | if not record.exc_text: |
---|
583 | n/a | record.exc_text = self.formatException(record.exc_info) |
---|
584 | n/a | if record.exc_text: |
---|
585 | n/a | if s[-1:] != "\n": |
---|
586 | n/a | s = s + "\n" |
---|
587 | n/a | s = s + record.exc_text |
---|
588 | n/a | if record.stack_info: |
---|
589 | n/a | if s[-1:] != "\n": |
---|
590 | n/a | s = s + "\n" |
---|
591 | n/a | s = s + self.formatStack(record.stack_info) |
---|
592 | n/a | return s |
---|
593 | n/a | |
---|
594 | n/a | # |
---|
595 | n/a | # The default formatter to use when no other is specified |
---|
596 | n/a | # |
---|
597 | n/a | _defaultFormatter = Formatter() |
---|
598 | n/a | |
---|
599 | n/a | class BufferingFormatter(object): |
---|
600 | n/a | """ |
---|
601 | n/a | A formatter suitable for formatting a number of records. |
---|
602 | n/a | """ |
---|
603 | n/a | def __init__(self, linefmt=None): |
---|
604 | n/a | """ |
---|
605 | n/a | Optionally specify a formatter which will be used to format each |
---|
606 | n/a | individual record. |
---|
607 | n/a | """ |
---|
608 | n/a | if linefmt: |
---|
609 | n/a | self.linefmt = linefmt |
---|
610 | n/a | else: |
---|
611 | n/a | self.linefmt = _defaultFormatter |
---|
612 | n/a | |
---|
613 | n/a | def formatHeader(self, records): |
---|
614 | n/a | """ |
---|
615 | n/a | Return the header string for the specified records. |
---|
616 | n/a | """ |
---|
617 | n/a | return "" |
---|
618 | n/a | |
---|
619 | n/a | def formatFooter(self, records): |
---|
620 | n/a | """ |
---|
621 | n/a | Return the footer string for the specified records. |
---|
622 | n/a | """ |
---|
623 | n/a | return "" |
---|
624 | n/a | |
---|
625 | n/a | def format(self, records): |
---|
626 | n/a | """ |
---|
627 | n/a | Format the specified records and return the result as a string. |
---|
628 | n/a | """ |
---|
629 | n/a | rv = "" |
---|
630 | n/a | if len(records) > 0: |
---|
631 | n/a | rv = rv + self.formatHeader(records) |
---|
632 | n/a | for record in records: |
---|
633 | n/a | rv = rv + self.linefmt.format(record) |
---|
634 | n/a | rv = rv + self.formatFooter(records) |
---|
635 | n/a | return rv |
---|
636 | n/a | |
---|
637 | n/a | #--------------------------------------------------------------------------- |
---|
638 | n/a | # Filter classes and functions |
---|
639 | n/a | #--------------------------------------------------------------------------- |
---|
640 | n/a | |
---|
641 | n/a | class Filter(object): |
---|
642 | n/a | """ |
---|
643 | n/a | Filter instances are used to perform arbitrary filtering of LogRecords. |
---|
644 | n/a | |
---|
645 | n/a | Loggers and Handlers can optionally use Filter instances to filter |
---|
646 | n/a | records as desired. The base filter class only allows events which are |
---|
647 | n/a | below a certain point in the logger hierarchy. For example, a filter |
---|
648 | n/a | initialized with "A.B" will allow events logged by loggers "A.B", |
---|
649 | n/a | "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If |
---|
650 | n/a | initialized with the empty string, all events are passed. |
---|
651 | n/a | """ |
---|
652 | n/a | def __init__(self, name=''): |
---|
653 | n/a | """ |
---|
654 | n/a | Initialize a filter. |
---|
655 | n/a | |
---|
656 | n/a | Initialize with the name of the logger which, together with its |
---|
657 | n/a | children, will have its events allowed through the filter. If no |
---|
658 | n/a | name is specified, allow every event. |
---|
659 | n/a | """ |
---|
660 | n/a | self.name = name |
---|
661 | n/a | self.nlen = len(name) |
---|
662 | n/a | |
---|
663 | n/a | def filter(self, record): |
---|
664 | n/a | """ |
---|
665 | n/a | Determine if the specified record is to be logged. |
---|
666 | n/a | |
---|
667 | n/a | Is the specified record to be logged? Returns 0 for no, nonzero for |
---|
668 | n/a | yes. If deemed appropriate, the record may be modified in-place. |
---|
669 | n/a | """ |
---|
670 | n/a | if self.nlen == 0: |
---|
671 | n/a | return True |
---|
672 | n/a | elif self.name == record.name: |
---|
673 | n/a | return True |
---|
674 | n/a | elif record.name.find(self.name, 0, self.nlen) != 0: |
---|
675 | n/a | return False |
---|
676 | n/a | return (record.name[self.nlen] == ".") |
---|
677 | n/a | |
---|
678 | n/a | class Filterer(object): |
---|
679 | n/a | """ |
---|
680 | n/a | A base class for loggers and handlers which allows them to share |
---|
681 | n/a | common code. |
---|
682 | n/a | """ |
---|
683 | n/a | def __init__(self): |
---|
684 | n/a | """ |
---|
685 | n/a | Initialize the list of filters to be an empty list. |
---|
686 | n/a | """ |
---|
687 | n/a | self.filters = [] |
---|
688 | n/a | |
---|
689 | n/a | def addFilter(self, filter): |
---|
690 | n/a | """ |
---|
691 | n/a | Add the specified filter to this handler. |
---|
692 | n/a | """ |
---|
693 | n/a | if not (filter in self.filters): |
---|
694 | n/a | self.filters.append(filter) |
---|
695 | n/a | |
---|
696 | n/a | def removeFilter(self, filter): |
---|
697 | n/a | """ |
---|
698 | n/a | Remove the specified filter from this handler. |
---|
699 | n/a | """ |
---|
700 | n/a | if filter in self.filters: |
---|
701 | n/a | self.filters.remove(filter) |
---|
702 | n/a | |
---|
703 | n/a | def filter(self, record): |
---|
704 | n/a | """ |
---|
705 | n/a | Determine if a record is loggable by consulting all the filters. |
---|
706 | n/a | |
---|
707 | n/a | The default is to allow the record to be logged; any filter can veto |
---|
708 | n/a | this and the record is then dropped. Returns a zero value if a record |
---|
709 | n/a | is to be dropped, else non-zero. |
---|
710 | n/a | |
---|
711 | n/a | .. versionchanged:: 3.2 |
---|
712 | n/a | |
---|
713 | n/a | Allow filters to be just callables. |
---|
714 | n/a | """ |
---|
715 | n/a | rv = True |
---|
716 | n/a | for f in self.filters: |
---|
717 | n/a | if hasattr(f, 'filter'): |
---|
718 | n/a | result = f.filter(record) |
---|
719 | n/a | else: |
---|
720 | n/a | result = f(record) # assume callable - will raise if not |
---|
721 | n/a | if not result: |
---|
722 | n/a | rv = False |
---|
723 | n/a | break |
---|
724 | n/a | return rv |
---|
725 | n/a | |
---|
726 | n/a | #--------------------------------------------------------------------------- |
---|
727 | n/a | # Handler classes and functions |
---|
728 | n/a | #--------------------------------------------------------------------------- |
---|
729 | n/a | |
---|
730 | n/a | _handlers = weakref.WeakValueDictionary() #map of handler names to handlers |
---|
731 | n/a | _handlerList = [] # added to allow handlers to be removed in reverse of order initialized |
---|
732 | n/a | |
---|
733 | n/a | def _removeHandlerRef(wr): |
---|
734 | n/a | """ |
---|
735 | n/a | Remove a handler reference from the internal cleanup list. |
---|
736 | n/a | """ |
---|
737 | n/a | # This function can be called during module teardown, when globals are |
---|
738 | n/a | # set to None. It can also be called from another thread. So we need to |
---|
739 | n/a | # pre-emptively grab the necessary globals and check if they're None, |
---|
740 | n/a | # to prevent race conditions and failures during interpreter shutdown. |
---|
741 | n/a | acquire, release, handlers = _acquireLock, _releaseLock, _handlerList |
---|
742 | n/a | if acquire and release and handlers: |
---|
743 | n/a | acquire() |
---|
744 | n/a | try: |
---|
745 | n/a | if wr in handlers: |
---|
746 | n/a | handlers.remove(wr) |
---|
747 | n/a | finally: |
---|
748 | n/a | release() |
---|
749 | n/a | |
---|
750 | n/a | def _addHandlerRef(handler): |
---|
751 | n/a | """ |
---|
752 | n/a | Add a handler to the internal cleanup list using a weak reference. |
---|
753 | n/a | """ |
---|
754 | n/a | _acquireLock() |
---|
755 | n/a | try: |
---|
756 | n/a | _handlerList.append(weakref.ref(handler, _removeHandlerRef)) |
---|
757 | n/a | finally: |
---|
758 | n/a | _releaseLock() |
---|
759 | n/a | |
---|
760 | n/a | class Handler(Filterer): |
---|
761 | n/a | """ |
---|
762 | n/a | Handler instances dispatch logging events to specific destinations. |
---|
763 | n/a | |
---|
764 | n/a | The base handler class. Acts as a placeholder which defines the Handler |
---|
765 | n/a | interface. Handlers can optionally use Formatter instances to format |
---|
766 | n/a | records as desired. By default, no formatter is specified; in this case, |
---|
767 | n/a | the 'raw' message as determined by record.message is logged. |
---|
768 | n/a | """ |
---|
769 | n/a | def __init__(self, level=NOTSET): |
---|
770 | n/a | """ |
---|
771 | n/a | Initializes the instance - basically setting the formatter to None |
---|
772 | n/a | and the filter list to empty. |
---|
773 | n/a | """ |
---|
774 | n/a | Filterer.__init__(self) |
---|
775 | n/a | self._name = None |
---|
776 | n/a | self.level = _checkLevel(level) |
---|
777 | n/a | self.formatter = None |
---|
778 | n/a | # Add the handler to the global _handlerList (for cleanup on shutdown) |
---|
779 | n/a | _addHandlerRef(self) |
---|
780 | n/a | self.createLock() |
---|
781 | n/a | |
---|
782 | n/a | def get_name(self): |
---|
783 | n/a | return self._name |
---|
784 | n/a | |
---|
785 | n/a | def set_name(self, name): |
---|
786 | n/a | _acquireLock() |
---|
787 | n/a | try: |
---|
788 | n/a | if self._name in _handlers: |
---|
789 | n/a | del _handlers[self._name] |
---|
790 | n/a | self._name = name |
---|
791 | n/a | if name: |
---|
792 | n/a | _handlers[name] = self |
---|
793 | n/a | finally: |
---|
794 | n/a | _releaseLock() |
---|
795 | n/a | |
---|
796 | n/a | name = property(get_name, set_name) |
---|
797 | n/a | |
---|
798 | n/a | def createLock(self): |
---|
799 | n/a | """ |
---|
800 | n/a | Acquire a thread lock for serializing access to the underlying I/O. |
---|
801 | n/a | """ |
---|
802 | n/a | if threading: |
---|
803 | n/a | self.lock = threading.RLock() |
---|
804 | n/a | else: #pragma: no cover |
---|
805 | n/a | self.lock = None |
---|
806 | n/a | |
---|
807 | n/a | def acquire(self): |
---|
808 | n/a | """ |
---|
809 | n/a | Acquire the I/O thread lock. |
---|
810 | n/a | """ |
---|
811 | n/a | if self.lock: |
---|
812 | n/a | self.lock.acquire() |
---|
813 | n/a | |
---|
814 | n/a | def release(self): |
---|
815 | n/a | """ |
---|
816 | n/a | Release the I/O thread lock. |
---|
817 | n/a | """ |
---|
818 | n/a | if self.lock: |
---|
819 | n/a | self.lock.release() |
---|
820 | n/a | |
---|
821 | n/a | def setLevel(self, level): |
---|
822 | n/a | """ |
---|
823 | n/a | Set the logging level of this handler. level must be an int or a str. |
---|
824 | n/a | """ |
---|
825 | n/a | self.level = _checkLevel(level) |
---|
826 | n/a | |
---|
827 | n/a | def format(self, record): |
---|
828 | n/a | """ |
---|
829 | n/a | Format the specified record. |
---|
830 | n/a | |
---|
831 | n/a | If a formatter is set, use it. Otherwise, use the default formatter |
---|
832 | n/a | for the module. |
---|
833 | n/a | """ |
---|
834 | n/a | if self.formatter: |
---|
835 | n/a | fmt = self.formatter |
---|
836 | n/a | else: |
---|
837 | n/a | fmt = _defaultFormatter |
---|
838 | n/a | return fmt.format(record) |
---|
839 | n/a | |
---|
840 | n/a | def emit(self, record): |
---|
841 | n/a | """ |
---|
842 | n/a | Do whatever it takes to actually log the specified logging record. |
---|
843 | n/a | |
---|
844 | n/a | This version is intended to be implemented by subclasses and so |
---|
845 | n/a | raises a NotImplementedError. |
---|
846 | n/a | """ |
---|
847 | n/a | raise NotImplementedError('emit must be implemented ' |
---|
848 | n/a | 'by Handler subclasses') |
---|
849 | n/a | |
---|
850 | n/a | def handle(self, record): |
---|
851 | n/a | """ |
---|
852 | n/a | Conditionally emit the specified logging record. |
---|
853 | n/a | |
---|
854 | n/a | Emission depends on filters which may have been added to the handler. |
---|
855 | n/a | Wrap the actual emission of the record with acquisition/release of |
---|
856 | n/a | the I/O thread lock. Returns whether the filter passed the record for |
---|
857 | n/a | emission. |
---|
858 | n/a | """ |
---|
859 | n/a | rv = self.filter(record) |
---|
860 | n/a | if rv: |
---|
861 | n/a | self.acquire() |
---|
862 | n/a | try: |
---|
863 | n/a | self.emit(record) |
---|
864 | n/a | finally: |
---|
865 | n/a | self.release() |
---|
866 | n/a | return rv |
---|
867 | n/a | |
---|
868 | n/a | def setFormatter(self, fmt): |
---|
869 | n/a | """ |
---|
870 | n/a | Set the formatter for this handler. |
---|
871 | n/a | """ |
---|
872 | n/a | self.formatter = fmt |
---|
873 | n/a | |
---|
874 | n/a | def flush(self): |
---|
875 | n/a | """ |
---|
876 | n/a | Ensure all logging output has been flushed. |
---|
877 | n/a | |
---|
878 | n/a | This version does nothing and is intended to be implemented by |
---|
879 | n/a | subclasses. |
---|
880 | n/a | """ |
---|
881 | n/a | pass |
---|
882 | n/a | |
---|
883 | n/a | def close(self): |
---|
884 | n/a | """ |
---|
885 | n/a | Tidy up any resources used by the handler. |
---|
886 | n/a | |
---|
887 | n/a | This version removes the handler from an internal map of handlers, |
---|
888 | n/a | _handlers, which is used for handler lookup by name. Subclasses |
---|
889 | n/a | should ensure that this gets called from overridden close() |
---|
890 | n/a | methods. |
---|
891 | n/a | """ |
---|
892 | n/a | #get the module data lock, as we're updating a shared structure. |
---|
893 | n/a | _acquireLock() |
---|
894 | n/a | try: #unlikely to raise an exception, but you never know... |
---|
895 | n/a | if self._name and self._name in _handlers: |
---|
896 | n/a | del _handlers[self._name] |
---|
897 | n/a | finally: |
---|
898 | n/a | _releaseLock() |
---|
899 | n/a | |
---|
900 | n/a | def handleError(self, record): |
---|
901 | n/a | """ |
---|
902 | n/a | Handle errors which occur during an emit() call. |
---|
903 | n/a | |
---|
904 | n/a | This method should be called from handlers when an exception is |
---|
905 | n/a | encountered during an emit() call. If raiseExceptions is false, |
---|
906 | n/a | exceptions get silently ignored. This is what is mostly wanted |
---|
907 | n/a | for a logging system - most users will not care about errors in |
---|
908 | n/a | the logging system, they are more interested in application errors. |
---|
909 | n/a | You could, however, replace this with a custom handler if you wish. |
---|
910 | n/a | The record which was being processed is passed in to this method. |
---|
911 | n/a | """ |
---|
912 | n/a | if raiseExceptions and sys.stderr: # see issue 13807 |
---|
913 | n/a | t, v, tb = sys.exc_info() |
---|
914 | n/a | try: |
---|
915 | n/a | sys.stderr.write('--- Logging error ---\n') |
---|
916 | n/a | traceback.print_exception(t, v, tb, None, sys.stderr) |
---|
917 | n/a | sys.stderr.write('Call stack:\n') |
---|
918 | n/a | # Walk the stack frame up until we're out of logging, |
---|
919 | n/a | # so as to print the calling context. |
---|
920 | n/a | frame = tb.tb_frame |
---|
921 | n/a | while (frame and os.path.dirname(frame.f_code.co_filename) == |
---|
922 | n/a | __path__[0]): |
---|
923 | n/a | frame = frame.f_back |
---|
924 | n/a | if frame: |
---|
925 | n/a | traceback.print_stack(frame, file=sys.stderr) |
---|
926 | n/a | else: |
---|
927 | n/a | # couldn't find the right stack frame, for some reason |
---|
928 | n/a | sys.stderr.write('Logged from file %s, line %s\n' % ( |
---|
929 | n/a | record.filename, record.lineno)) |
---|
930 | n/a | # Issue 18671: output logging message and arguments |
---|
931 | n/a | try: |
---|
932 | n/a | sys.stderr.write('Message: %r\n' |
---|
933 | n/a | 'Arguments: %s\n' % (record.msg, |
---|
934 | n/a | record.args)) |
---|
935 | n/a | except Exception: |
---|
936 | n/a | sys.stderr.write('Unable to print the message and arguments' |
---|
937 | n/a | ' - possible formatting error.\nUse the' |
---|
938 | n/a | ' traceback above to help find the error.\n' |
---|
939 | n/a | ) |
---|
940 | n/a | except OSError: #pragma: no cover |
---|
941 | n/a | pass # see issue 5971 |
---|
942 | n/a | finally: |
---|
943 | n/a | del t, v, tb |
---|
944 | n/a | |
---|
945 | n/a | def __repr__(self): |
---|
946 | n/a | level = getLevelName(self.level) |
---|
947 | n/a | return '<%s (%s)>' % (self.__class__.__name__, level) |
---|
948 | n/a | |
---|
949 | n/a | class StreamHandler(Handler): |
---|
950 | n/a | """ |
---|
951 | n/a | A handler class which writes logging records, appropriately formatted, |
---|
952 | n/a | to a stream. Note that this class does not close the stream, as |
---|
953 | n/a | sys.stdout or sys.stderr may be used. |
---|
954 | n/a | """ |
---|
955 | n/a | |
---|
956 | n/a | terminator = '\n' |
---|
957 | n/a | |
---|
958 | n/a | def __init__(self, stream=None): |
---|
959 | n/a | """ |
---|
960 | n/a | Initialize the handler. |
---|
961 | n/a | |
---|
962 | n/a | If stream is not specified, sys.stderr is used. |
---|
963 | n/a | """ |
---|
964 | n/a | Handler.__init__(self) |
---|
965 | n/a | if stream is None: |
---|
966 | n/a | stream = sys.stderr |
---|
967 | n/a | self.stream = stream |
---|
968 | n/a | |
---|
969 | n/a | def flush(self): |
---|
970 | n/a | """ |
---|
971 | n/a | Flushes the stream. |
---|
972 | n/a | """ |
---|
973 | n/a | self.acquire() |
---|
974 | n/a | try: |
---|
975 | n/a | if self.stream and hasattr(self.stream, "flush"): |
---|
976 | n/a | self.stream.flush() |
---|
977 | n/a | finally: |
---|
978 | n/a | self.release() |
---|
979 | n/a | |
---|
980 | n/a | def emit(self, record): |
---|
981 | n/a | """ |
---|
982 | n/a | Emit a record. |
---|
983 | n/a | |
---|
984 | n/a | If a formatter is specified, it is used to format the record. |
---|
985 | n/a | The record is then written to the stream with a trailing newline. If |
---|
986 | n/a | exception information is present, it is formatted using |
---|
987 | n/a | traceback.print_exception and appended to the stream. If the stream |
---|
988 | n/a | has an 'encoding' attribute, it is used to determine how to do the |
---|
989 | n/a | output to the stream. |
---|
990 | n/a | """ |
---|
991 | n/a | try: |
---|
992 | n/a | msg = self.format(record) |
---|
993 | n/a | stream = self.stream |
---|
994 | n/a | stream.write(msg) |
---|
995 | n/a | stream.write(self.terminator) |
---|
996 | n/a | self.flush() |
---|
997 | n/a | except Exception: |
---|
998 | n/a | self.handleError(record) |
---|
999 | n/a | |
---|
1000 | n/a | def __repr__(self): |
---|
1001 | n/a | level = getLevelName(self.level) |
---|
1002 | n/a | name = getattr(self.stream, 'name', '') |
---|
1003 | n/a | if name: |
---|
1004 | n/a | name += ' ' |
---|
1005 | n/a | return '<%s %s(%s)>' % (self.__class__.__name__, name, level) |
---|
1006 | n/a | |
---|
1007 | n/a | |
---|
1008 | n/a | class FileHandler(StreamHandler): |
---|
1009 | n/a | """ |
---|
1010 | n/a | A handler class which writes formatted logging records to disk files. |
---|
1011 | n/a | """ |
---|
1012 | n/a | def __init__(self, filename, mode='a', encoding=None, delay=False): |
---|
1013 | n/a | """ |
---|
1014 | n/a | Open the specified file and use it as the stream for logging. |
---|
1015 | n/a | """ |
---|
1016 | n/a | # Issue #27493: add support for Path objects to be passed in |
---|
1017 | n/a | filename = os.fspath(filename) |
---|
1018 | n/a | #keep the absolute path, otherwise derived classes which use this |
---|
1019 | n/a | #may come a cropper when the current directory changes |
---|
1020 | n/a | self.baseFilename = os.path.abspath(filename) |
---|
1021 | n/a | self.mode = mode |
---|
1022 | n/a | self.encoding = encoding |
---|
1023 | n/a | self.delay = delay |
---|
1024 | n/a | if delay: |
---|
1025 | n/a | #We don't open the stream, but we still need to call the |
---|
1026 | n/a | #Handler constructor to set level, formatter, lock etc. |
---|
1027 | n/a | Handler.__init__(self) |
---|
1028 | n/a | self.stream = None |
---|
1029 | n/a | else: |
---|
1030 | n/a | StreamHandler.__init__(self, self._open()) |
---|
1031 | n/a | |
---|
1032 | n/a | def close(self): |
---|
1033 | n/a | """ |
---|
1034 | n/a | Closes the stream. |
---|
1035 | n/a | """ |
---|
1036 | n/a | self.acquire() |
---|
1037 | n/a | try: |
---|
1038 | n/a | try: |
---|
1039 | n/a | if self.stream: |
---|
1040 | n/a | try: |
---|
1041 | n/a | self.flush() |
---|
1042 | n/a | finally: |
---|
1043 | n/a | stream = self.stream |
---|
1044 | n/a | self.stream = None |
---|
1045 | n/a | if hasattr(stream, "close"): |
---|
1046 | n/a | stream.close() |
---|
1047 | n/a | finally: |
---|
1048 | n/a | # Issue #19523: call unconditionally to |
---|
1049 | n/a | # prevent a handler leak when delay is set |
---|
1050 | n/a | StreamHandler.close(self) |
---|
1051 | n/a | finally: |
---|
1052 | n/a | self.release() |
---|
1053 | n/a | |
---|
1054 | n/a | def _open(self): |
---|
1055 | n/a | """ |
---|
1056 | n/a | Open the current base file with the (original) mode and encoding. |
---|
1057 | n/a | Return the resulting stream. |
---|
1058 | n/a | """ |
---|
1059 | n/a | return open(self.baseFilename, self.mode, encoding=self.encoding) |
---|
1060 | n/a | |
---|
1061 | n/a | def emit(self, record): |
---|
1062 | n/a | """ |
---|
1063 | n/a | Emit a record. |
---|
1064 | n/a | |
---|
1065 | n/a | If the stream was not opened because 'delay' was specified in the |
---|
1066 | n/a | constructor, open it before calling the superclass's emit. |
---|
1067 | n/a | """ |
---|
1068 | n/a | if self.stream is None: |
---|
1069 | n/a | self.stream = self._open() |
---|
1070 | n/a | StreamHandler.emit(self, record) |
---|
1071 | n/a | |
---|
1072 | n/a | def __repr__(self): |
---|
1073 | n/a | level = getLevelName(self.level) |
---|
1074 | n/a | return '<%s %s (%s)>' % (self.__class__.__name__, self.baseFilename, level) |
---|
1075 | n/a | |
---|
1076 | n/a | |
---|
1077 | n/a | class _StderrHandler(StreamHandler): |
---|
1078 | n/a | """ |
---|
1079 | n/a | This class is like a StreamHandler using sys.stderr, but always uses |
---|
1080 | n/a | whatever sys.stderr is currently set to rather than the value of |
---|
1081 | n/a | sys.stderr at handler construction time. |
---|
1082 | n/a | """ |
---|
1083 | n/a | def __init__(self, level=NOTSET): |
---|
1084 | n/a | """ |
---|
1085 | n/a | Initialize the handler. |
---|
1086 | n/a | """ |
---|
1087 | n/a | Handler.__init__(self, level) |
---|
1088 | n/a | |
---|
1089 | n/a | @property |
---|
1090 | n/a | def stream(self): |
---|
1091 | n/a | return sys.stderr |
---|
1092 | n/a | |
---|
1093 | n/a | |
---|
1094 | n/a | _defaultLastResort = _StderrHandler(WARNING) |
---|
1095 | n/a | lastResort = _defaultLastResort |
---|
1096 | n/a | |
---|
1097 | n/a | #--------------------------------------------------------------------------- |
---|
1098 | n/a | # Manager classes and functions |
---|
1099 | n/a | #--------------------------------------------------------------------------- |
---|
1100 | n/a | |
---|
1101 | n/a | class PlaceHolder(object): |
---|
1102 | n/a | """ |
---|
1103 | n/a | PlaceHolder instances are used in the Manager logger hierarchy to take |
---|
1104 | n/a | the place of nodes for which no loggers have been defined. This class is |
---|
1105 | n/a | intended for internal use only and not as part of the public API. |
---|
1106 | n/a | """ |
---|
1107 | n/a | def __init__(self, alogger): |
---|
1108 | n/a | """ |
---|
1109 | n/a | Initialize with the specified logger being a child of this placeholder. |
---|
1110 | n/a | """ |
---|
1111 | n/a | self.loggerMap = { alogger : None } |
---|
1112 | n/a | |
---|
1113 | n/a | def append(self, alogger): |
---|
1114 | n/a | """ |
---|
1115 | n/a | Add the specified logger as a child of this placeholder. |
---|
1116 | n/a | """ |
---|
1117 | n/a | if alogger not in self.loggerMap: |
---|
1118 | n/a | self.loggerMap[alogger] = None |
---|
1119 | n/a | |
---|
1120 | n/a | # |
---|
1121 | n/a | # Determine which class to use when instantiating loggers. |
---|
1122 | n/a | # |
---|
1123 | n/a | |
---|
1124 | n/a | def setLoggerClass(klass): |
---|
1125 | n/a | """ |
---|
1126 | n/a | Set the class to be used when instantiating a logger. The class should |
---|
1127 | n/a | define __init__() such that only a name argument is required, and the |
---|
1128 | n/a | __init__() should call Logger.__init__() |
---|
1129 | n/a | """ |
---|
1130 | n/a | if klass != Logger: |
---|
1131 | n/a | if not issubclass(klass, Logger): |
---|
1132 | n/a | raise TypeError("logger not derived from logging.Logger: " |
---|
1133 | n/a | + klass.__name__) |
---|
1134 | n/a | global _loggerClass |
---|
1135 | n/a | _loggerClass = klass |
---|
1136 | n/a | |
---|
1137 | n/a | def getLoggerClass(): |
---|
1138 | n/a | """ |
---|
1139 | n/a | Return the class to be used when instantiating a logger. |
---|
1140 | n/a | """ |
---|
1141 | n/a | return _loggerClass |
---|
1142 | n/a | |
---|
1143 | n/a | class Manager(object): |
---|
1144 | n/a | """ |
---|
1145 | n/a | There is [under normal circumstances] just one Manager instance, which |
---|
1146 | n/a | holds the hierarchy of loggers. |
---|
1147 | n/a | """ |
---|
1148 | n/a | def __init__(self, rootnode): |
---|
1149 | n/a | """ |
---|
1150 | n/a | Initialize the manager with the root node of the logger hierarchy. |
---|
1151 | n/a | """ |
---|
1152 | n/a | self.root = rootnode |
---|
1153 | n/a | self.disable = 0 |
---|
1154 | n/a | self.emittedNoHandlerWarning = False |
---|
1155 | n/a | self.loggerDict = {} |
---|
1156 | n/a | self.loggerClass = None |
---|
1157 | n/a | self.logRecordFactory = None |
---|
1158 | n/a | |
---|
1159 | n/a | def getLogger(self, name): |
---|
1160 | n/a | """ |
---|
1161 | n/a | Get a logger with the specified name (channel name), creating it |
---|
1162 | n/a | if it doesn't yet exist. This name is a dot-separated hierarchical |
---|
1163 | n/a | name, such as "a", "a.b", "a.b.c" or similar. |
---|
1164 | n/a | |
---|
1165 | n/a | If a PlaceHolder existed for the specified name [i.e. the logger |
---|
1166 | n/a | didn't exist but a child of it did], replace it with the created |
---|
1167 | n/a | logger and fix up the parent/child references which pointed to the |
---|
1168 | n/a | placeholder to now point to the logger. |
---|
1169 | n/a | """ |
---|
1170 | n/a | rv = None |
---|
1171 | n/a | if not isinstance(name, str): |
---|
1172 | n/a | raise TypeError('A logger name must be a string') |
---|
1173 | n/a | _acquireLock() |
---|
1174 | n/a | try: |
---|
1175 | n/a | if name in self.loggerDict: |
---|
1176 | n/a | rv = self.loggerDict[name] |
---|
1177 | n/a | if isinstance(rv, PlaceHolder): |
---|
1178 | n/a | ph = rv |
---|
1179 | n/a | rv = (self.loggerClass or _loggerClass)(name) |
---|
1180 | n/a | rv.manager = self |
---|
1181 | n/a | self.loggerDict[name] = rv |
---|
1182 | n/a | self._fixupChildren(ph, rv) |
---|
1183 | n/a | self._fixupParents(rv) |
---|
1184 | n/a | else: |
---|
1185 | n/a | rv = (self.loggerClass or _loggerClass)(name) |
---|
1186 | n/a | rv.manager = self |
---|
1187 | n/a | self.loggerDict[name] = rv |
---|
1188 | n/a | self._fixupParents(rv) |
---|
1189 | n/a | finally: |
---|
1190 | n/a | _releaseLock() |
---|
1191 | n/a | return rv |
---|
1192 | n/a | |
---|
1193 | n/a | def setLoggerClass(self, klass): |
---|
1194 | n/a | """ |
---|
1195 | n/a | Set the class to be used when instantiating a logger with this Manager. |
---|
1196 | n/a | """ |
---|
1197 | n/a | if klass != Logger: |
---|
1198 | n/a | if not issubclass(klass, Logger): |
---|
1199 | n/a | raise TypeError("logger not derived from logging.Logger: " |
---|
1200 | n/a | + klass.__name__) |
---|
1201 | n/a | self.loggerClass = klass |
---|
1202 | n/a | |
---|
1203 | n/a | def setLogRecordFactory(self, factory): |
---|
1204 | n/a | """ |
---|
1205 | n/a | Set the factory to be used when instantiating a log record with this |
---|
1206 | n/a | Manager. |
---|
1207 | n/a | """ |
---|
1208 | n/a | self.logRecordFactory = factory |
---|
1209 | n/a | |
---|
1210 | n/a | def _fixupParents(self, alogger): |
---|
1211 | n/a | """ |
---|
1212 | n/a | Ensure that there are either loggers or placeholders all the way |
---|
1213 | n/a | from the specified logger to the root of the logger hierarchy. |
---|
1214 | n/a | """ |
---|
1215 | n/a | name = alogger.name |
---|
1216 | n/a | i = name.rfind(".") |
---|
1217 | n/a | rv = None |
---|
1218 | n/a | while (i > 0) and not rv: |
---|
1219 | n/a | substr = name[:i] |
---|
1220 | n/a | if substr not in self.loggerDict: |
---|
1221 | n/a | self.loggerDict[substr] = PlaceHolder(alogger) |
---|
1222 | n/a | else: |
---|
1223 | n/a | obj = self.loggerDict[substr] |
---|
1224 | n/a | if isinstance(obj, Logger): |
---|
1225 | n/a | rv = obj |
---|
1226 | n/a | else: |
---|
1227 | n/a | assert isinstance(obj, PlaceHolder) |
---|
1228 | n/a | obj.append(alogger) |
---|
1229 | n/a | i = name.rfind(".", 0, i - 1) |
---|
1230 | n/a | if not rv: |
---|
1231 | n/a | rv = self.root |
---|
1232 | n/a | alogger.parent = rv |
---|
1233 | n/a | |
---|
1234 | n/a | def _fixupChildren(self, ph, alogger): |
---|
1235 | n/a | """ |
---|
1236 | n/a | Ensure that children of the placeholder ph are connected to the |
---|
1237 | n/a | specified logger. |
---|
1238 | n/a | """ |
---|
1239 | n/a | name = alogger.name |
---|
1240 | n/a | namelen = len(name) |
---|
1241 | n/a | for c in ph.loggerMap.keys(): |
---|
1242 | n/a | #The if means ... if not c.parent.name.startswith(nm) |
---|
1243 | n/a | if c.parent.name[:namelen] != name: |
---|
1244 | n/a | alogger.parent = c.parent |
---|
1245 | n/a | c.parent = alogger |
---|
1246 | n/a | |
---|
1247 | n/a | #--------------------------------------------------------------------------- |
---|
1248 | n/a | # Logger classes and functions |
---|
1249 | n/a | #--------------------------------------------------------------------------- |
---|
1250 | n/a | |
---|
1251 | n/a | class Logger(Filterer): |
---|
1252 | n/a | """ |
---|
1253 | n/a | Instances of the Logger class represent a single logging channel. A |
---|
1254 | n/a | "logging channel" indicates an area of an application. Exactly how an |
---|
1255 | n/a | "area" is defined is up to the application developer. Since an |
---|
1256 | n/a | application can have any number of areas, logging channels are identified |
---|
1257 | n/a | by a unique string. Application areas can be nested (e.g. an area |
---|
1258 | n/a | of "input processing" might include sub-areas "read CSV files", "read |
---|
1259 | n/a | XLS files" and "read Gnumeric files"). To cater for this natural nesting, |
---|
1260 | n/a | channel names are organized into a namespace hierarchy where levels are |
---|
1261 | n/a | separated by periods, much like the Java or Python package namespace. So |
---|
1262 | n/a | in the instance given above, channel names might be "input" for the upper |
---|
1263 | n/a | level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. |
---|
1264 | n/a | There is no arbitrary limit to the depth of nesting. |
---|
1265 | n/a | """ |
---|
1266 | n/a | def __init__(self, name, level=NOTSET): |
---|
1267 | n/a | """ |
---|
1268 | n/a | Initialize the logger with a name and an optional level. |
---|
1269 | n/a | """ |
---|
1270 | n/a | Filterer.__init__(self) |
---|
1271 | n/a | self.name = name |
---|
1272 | n/a | self.level = _checkLevel(level) |
---|
1273 | n/a | self.parent = None |
---|
1274 | n/a | self.propagate = True |
---|
1275 | n/a | self.handlers = [] |
---|
1276 | n/a | self.disabled = False |
---|
1277 | n/a | |
---|
1278 | n/a | def setLevel(self, level): |
---|
1279 | n/a | """ |
---|
1280 | n/a | Set the logging level of this logger. level must be an int or a str. |
---|
1281 | n/a | """ |
---|
1282 | n/a | self.level = _checkLevel(level) |
---|
1283 | n/a | |
---|
1284 | n/a | def debug(self, msg, *args, **kwargs): |
---|
1285 | n/a | """ |
---|
1286 | n/a | Log 'msg % args' with severity 'DEBUG'. |
---|
1287 | n/a | |
---|
1288 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1289 | n/a | a true value, e.g. |
---|
1290 | n/a | |
---|
1291 | n/a | logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) |
---|
1292 | n/a | """ |
---|
1293 | n/a | if self.isEnabledFor(DEBUG): |
---|
1294 | n/a | self._log(DEBUG, msg, args, **kwargs) |
---|
1295 | n/a | |
---|
1296 | n/a | def info(self, msg, *args, **kwargs): |
---|
1297 | n/a | """ |
---|
1298 | n/a | Log 'msg % args' with severity 'INFO'. |
---|
1299 | n/a | |
---|
1300 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1301 | n/a | a true value, e.g. |
---|
1302 | n/a | |
---|
1303 | n/a | logger.info("Houston, we have a %s", "interesting problem", exc_info=1) |
---|
1304 | n/a | """ |
---|
1305 | n/a | if self.isEnabledFor(INFO): |
---|
1306 | n/a | self._log(INFO, msg, args, **kwargs) |
---|
1307 | n/a | |
---|
1308 | n/a | def warning(self, msg, *args, **kwargs): |
---|
1309 | n/a | """ |
---|
1310 | n/a | Log 'msg % args' with severity 'WARNING'. |
---|
1311 | n/a | |
---|
1312 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1313 | n/a | a true value, e.g. |
---|
1314 | n/a | |
---|
1315 | n/a | logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1) |
---|
1316 | n/a | """ |
---|
1317 | n/a | if self.isEnabledFor(WARNING): |
---|
1318 | n/a | self._log(WARNING, msg, args, **kwargs) |
---|
1319 | n/a | |
---|
1320 | n/a | def warn(self, msg, *args, **kwargs): |
---|
1321 | n/a | warnings.warn("The 'warn' method is deprecated, " |
---|
1322 | n/a | "use 'warning' instead", DeprecationWarning, 2) |
---|
1323 | n/a | self.warning(msg, *args, **kwargs) |
---|
1324 | n/a | |
---|
1325 | n/a | def error(self, msg, *args, **kwargs): |
---|
1326 | n/a | """ |
---|
1327 | n/a | Log 'msg % args' with severity 'ERROR'. |
---|
1328 | n/a | |
---|
1329 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1330 | n/a | a true value, e.g. |
---|
1331 | n/a | |
---|
1332 | n/a | logger.error("Houston, we have a %s", "major problem", exc_info=1) |
---|
1333 | n/a | """ |
---|
1334 | n/a | if self.isEnabledFor(ERROR): |
---|
1335 | n/a | self._log(ERROR, msg, args, **kwargs) |
---|
1336 | n/a | |
---|
1337 | n/a | def exception(self, msg, *args, exc_info=True, **kwargs): |
---|
1338 | n/a | """ |
---|
1339 | n/a | Convenience method for logging an ERROR with exception information. |
---|
1340 | n/a | """ |
---|
1341 | n/a | self.error(msg, *args, exc_info=exc_info, **kwargs) |
---|
1342 | n/a | |
---|
1343 | n/a | def critical(self, msg, *args, **kwargs): |
---|
1344 | n/a | """ |
---|
1345 | n/a | Log 'msg % args' with severity 'CRITICAL'. |
---|
1346 | n/a | |
---|
1347 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1348 | n/a | a true value, e.g. |
---|
1349 | n/a | |
---|
1350 | n/a | logger.critical("Houston, we have a %s", "major disaster", exc_info=1) |
---|
1351 | n/a | """ |
---|
1352 | n/a | if self.isEnabledFor(CRITICAL): |
---|
1353 | n/a | self._log(CRITICAL, msg, args, **kwargs) |
---|
1354 | n/a | |
---|
1355 | n/a | fatal = critical |
---|
1356 | n/a | |
---|
1357 | n/a | def log(self, level, msg, *args, **kwargs): |
---|
1358 | n/a | """ |
---|
1359 | n/a | Log 'msg % args' with the integer severity 'level'. |
---|
1360 | n/a | |
---|
1361 | n/a | To pass exception information, use the keyword argument exc_info with |
---|
1362 | n/a | a true value, e.g. |
---|
1363 | n/a | |
---|
1364 | n/a | logger.log(level, "We have a %s", "mysterious problem", exc_info=1) |
---|
1365 | n/a | """ |
---|
1366 | n/a | if not isinstance(level, int): |
---|
1367 | n/a | if raiseExceptions: |
---|
1368 | n/a | raise TypeError("level must be an integer") |
---|
1369 | n/a | else: |
---|
1370 | n/a | return |
---|
1371 | n/a | if self.isEnabledFor(level): |
---|
1372 | n/a | self._log(level, msg, args, **kwargs) |
---|
1373 | n/a | |
---|
1374 | n/a | def findCaller(self, stack_info=False): |
---|
1375 | n/a | """ |
---|
1376 | n/a | Find the stack frame of the caller so that we can note the source |
---|
1377 | n/a | file name, line number and function name. |
---|
1378 | n/a | """ |
---|
1379 | n/a | f = currentframe() |
---|
1380 | n/a | #On some versions of IronPython, currentframe() returns None if |
---|
1381 | n/a | #IronPython isn't run with -X:Frames. |
---|
1382 | n/a | if f is not None: |
---|
1383 | n/a | f = f.f_back |
---|
1384 | n/a | rv = "(unknown file)", 0, "(unknown function)", None |
---|
1385 | n/a | while hasattr(f, "f_code"): |
---|
1386 | n/a | co = f.f_code |
---|
1387 | n/a | filename = os.path.normcase(co.co_filename) |
---|
1388 | n/a | if filename == _srcfile: |
---|
1389 | n/a | f = f.f_back |
---|
1390 | n/a | continue |
---|
1391 | n/a | sinfo = None |
---|
1392 | n/a | if stack_info: |
---|
1393 | n/a | sio = io.StringIO() |
---|
1394 | n/a | sio.write('Stack (most recent call last):\n') |
---|
1395 | n/a | traceback.print_stack(f, file=sio) |
---|
1396 | n/a | sinfo = sio.getvalue() |
---|
1397 | n/a | if sinfo[-1] == '\n': |
---|
1398 | n/a | sinfo = sinfo[:-1] |
---|
1399 | n/a | sio.close() |
---|
1400 | n/a | rv = (co.co_filename, f.f_lineno, co.co_name, sinfo) |
---|
1401 | n/a | break |
---|
1402 | n/a | return rv |
---|
1403 | n/a | |
---|
1404 | n/a | def makeRecord(self, name, level, fn, lno, msg, args, exc_info, |
---|
1405 | n/a | func=None, extra=None, sinfo=None): |
---|
1406 | n/a | """ |
---|
1407 | n/a | A factory method which can be overridden in subclasses to create |
---|
1408 | n/a | specialized LogRecords. |
---|
1409 | n/a | """ |
---|
1410 | n/a | rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func, |
---|
1411 | n/a | sinfo) |
---|
1412 | n/a | if extra is not None: |
---|
1413 | n/a | for key in extra: |
---|
1414 | n/a | if (key in ["message", "asctime"]) or (key in rv.__dict__): |
---|
1415 | n/a | raise KeyError("Attempt to overwrite %r in LogRecord" % key) |
---|
1416 | n/a | rv.__dict__[key] = extra[key] |
---|
1417 | n/a | return rv |
---|
1418 | n/a | |
---|
1419 | n/a | def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False): |
---|
1420 | n/a | """ |
---|
1421 | n/a | Low-level logging routine which creates a LogRecord and then calls |
---|
1422 | n/a | all the handlers of this logger to handle the record. |
---|
1423 | n/a | """ |
---|
1424 | n/a | sinfo = None |
---|
1425 | n/a | if _srcfile: |
---|
1426 | n/a | #IronPython doesn't track Python frames, so findCaller raises an |
---|
1427 | n/a | #exception on some versions of IronPython. We trap it here so that |
---|
1428 | n/a | #IronPython can use logging. |
---|
1429 | n/a | try: |
---|
1430 | n/a | fn, lno, func, sinfo = self.findCaller(stack_info) |
---|
1431 | n/a | except ValueError: # pragma: no cover |
---|
1432 | n/a | fn, lno, func = "(unknown file)", 0, "(unknown function)" |
---|
1433 | n/a | else: # pragma: no cover |
---|
1434 | n/a | fn, lno, func = "(unknown file)", 0, "(unknown function)" |
---|
1435 | n/a | if exc_info: |
---|
1436 | n/a | if isinstance(exc_info, BaseException): |
---|
1437 | n/a | exc_info = (type(exc_info), exc_info, exc_info.__traceback__) |
---|
1438 | n/a | elif not isinstance(exc_info, tuple): |
---|
1439 | n/a | exc_info = sys.exc_info() |
---|
1440 | n/a | record = self.makeRecord(self.name, level, fn, lno, msg, args, |
---|
1441 | n/a | exc_info, func, extra, sinfo) |
---|
1442 | n/a | self.handle(record) |
---|
1443 | n/a | |
---|
1444 | n/a | def handle(self, record): |
---|
1445 | n/a | """ |
---|
1446 | n/a | Call the handlers for the specified record. |
---|
1447 | n/a | |
---|
1448 | n/a | This method is used for unpickled records received from a socket, as |
---|
1449 | n/a | well as those created locally. Logger-level filtering is applied. |
---|
1450 | n/a | """ |
---|
1451 | n/a | if (not self.disabled) and self.filter(record): |
---|
1452 | n/a | self.callHandlers(record) |
---|
1453 | n/a | |
---|
1454 | n/a | def addHandler(self, hdlr): |
---|
1455 | n/a | """ |
---|
1456 | n/a | Add the specified handler to this logger. |
---|
1457 | n/a | """ |
---|
1458 | n/a | _acquireLock() |
---|
1459 | n/a | try: |
---|
1460 | n/a | if not (hdlr in self.handlers): |
---|
1461 | n/a | self.handlers.append(hdlr) |
---|
1462 | n/a | finally: |
---|
1463 | n/a | _releaseLock() |
---|
1464 | n/a | |
---|
1465 | n/a | def removeHandler(self, hdlr): |
---|
1466 | n/a | """ |
---|
1467 | n/a | Remove the specified handler from this logger. |
---|
1468 | n/a | """ |
---|
1469 | n/a | _acquireLock() |
---|
1470 | n/a | try: |
---|
1471 | n/a | if hdlr in self.handlers: |
---|
1472 | n/a | self.handlers.remove(hdlr) |
---|
1473 | n/a | finally: |
---|
1474 | n/a | _releaseLock() |
---|
1475 | n/a | |
---|
1476 | n/a | def hasHandlers(self): |
---|
1477 | n/a | """ |
---|
1478 | n/a | See if this logger has any handlers configured. |
---|
1479 | n/a | |
---|
1480 | n/a | Loop through all handlers for this logger and its parents in the |
---|
1481 | n/a | logger hierarchy. Return True if a handler was found, else False. |
---|
1482 | n/a | Stop searching up the hierarchy whenever a logger with the "propagate" |
---|
1483 | n/a | attribute set to zero is found - that will be the last logger which |
---|
1484 | n/a | is checked for the existence of handlers. |
---|
1485 | n/a | """ |
---|
1486 | n/a | c = self |
---|
1487 | n/a | rv = False |
---|
1488 | n/a | while c: |
---|
1489 | n/a | if c.handlers: |
---|
1490 | n/a | rv = True |
---|
1491 | n/a | break |
---|
1492 | n/a | if not c.propagate: |
---|
1493 | n/a | break |
---|
1494 | n/a | else: |
---|
1495 | n/a | c = c.parent |
---|
1496 | n/a | return rv |
---|
1497 | n/a | |
---|
1498 | n/a | def callHandlers(self, record): |
---|
1499 | n/a | """ |
---|
1500 | n/a | Pass a record to all relevant handlers. |
---|
1501 | n/a | |
---|
1502 | n/a | Loop through all handlers for this logger and its parents in the |
---|
1503 | n/a | logger hierarchy. If no handler was found, output a one-off error |
---|
1504 | n/a | message to sys.stderr. Stop searching up the hierarchy whenever a |
---|
1505 | n/a | logger with the "propagate" attribute set to zero is found - that |
---|
1506 | n/a | will be the last logger whose handlers are called. |
---|
1507 | n/a | """ |
---|
1508 | n/a | c = self |
---|
1509 | n/a | found = 0 |
---|
1510 | n/a | while c: |
---|
1511 | n/a | for hdlr in c.handlers: |
---|
1512 | n/a | found = found + 1 |
---|
1513 | n/a | if record.levelno >= hdlr.level: |
---|
1514 | n/a | hdlr.handle(record) |
---|
1515 | n/a | if not c.propagate: |
---|
1516 | n/a | c = None #break out |
---|
1517 | n/a | else: |
---|
1518 | n/a | c = c.parent |
---|
1519 | n/a | if (found == 0): |
---|
1520 | n/a | if lastResort: |
---|
1521 | n/a | if record.levelno >= lastResort.level: |
---|
1522 | n/a | lastResort.handle(record) |
---|
1523 | n/a | elif raiseExceptions and not self.manager.emittedNoHandlerWarning: |
---|
1524 | n/a | sys.stderr.write("No handlers could be found for logger" |
---|
1525 | n/a | " \"%s\"\n" % self.name) |
---|
1526 | n/a | self.manager.emittedNoHandlerWarning = True |
---|
1527 | n/a | |
---|
1528 | n/a | def getEffectiveLevel(self): |
---|
1529 | n/a | """ |
---|
1530 | n/a | Get the effective level for this logger. |
---|
1531 | n/a | |
---|
1532 | n/a | Loop through this logger and its parents in the logger hierarchy, |
---|
1533 | n/a | looking for a non-zero logging level. Return the first one found. |
---|
1534 | n/a | """ |
---|
1535 | n/a | logger = self |
---|
1536 | n/a | while logger: |
---|
1537 | n/a | if logger.level: |
---|
1538 | n/a | return logger.level |
---|
1539 | n/a | logger = logger.parent |
---|
1540 | n/a | return NOTSET |
---|
1541 | n/a | |
---|
1542 | n/a | def isEnabledFor(self, level): |
---|
1543 | n/a | """ |
---|
1544 | n/a | Is this logger enabled for level 'level'? |
---|
1545 | n/a | """ |
---|
1546 | n/a | if self.manager.disable >= level: |
---|
1547 | n/a | return False |
---|
1548 | n/a | return level >= self.getEffectiveLevel() |
---|
1549 | n/a | |
---|
1550 | n/a | def getChild(self, suffix): |
---|
1551 | n/a | """ |
---|
1552 | n/a | Get a logger which is a descendant to this one. |
---|
1553 | n/a | |
---|
1554 | n/a | This is a convenience method, such that |
---|
1555 | n/a | |
---|
1556 | n/a | logging.getLogger('abc').getChild('def.ghi') |
---|
1557 | n/a | |
---|
1558 | n/a | is the same as |
---|
1559 | n/a | |
---|
1560 | n/a | logging.getLogger('abc.def.ghi') |
---|
1561 | n/a | |
---|
1562 | n/a | It's useful, for example, when the parent logger is named using |
---|
1563 | n/a | __name__ rather than a literal string. |
---|
1564 | n/a | """ |
---|
1565 | n/a | if self.root is not self: |
---|
1566 | n/a | suffix = '.'.join((self.name, suffix)) |
---|
1567 | n/a | return self.manager.getLogger(suffix) |
---|
1568 | n/a | |
---|
1569 | n/a | def __repr__(self): |
---|
1570 | n/a | level = getLevelName(self.getEffectiveLevel()) |
---|
1571 | n/a | return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level) |
---|
1572 | n/a | |
---|
1573 | n/a | |
---|
1574 | n/a | class RootLogger(Logger): |
---|
1575 | n/a | """ |
---|
1576 | n/a | A root logger is not that different to any other logger, except that |
---|
1577 | n/a | it must have a logging level and there is only one instance of it in |
---|
1578 | n/a | the hierarchy. |
---|
1579 | n/a | """ |
---|
1580 | n/a | def __init__(self, level): |
---|
1581 | n/a | """ |
---|
1582 | n/a | Initialize the logger with the name "root". |
---|
1583 | n/a | """ |
---|
1584 | n/a | Logger.__init__(self, "root", level) |
---|
1585 | n/a | |
---|
1586 | n/a | _loggerClass = Logger |
---|
1587 | n/a | |
---|
1588 | n/a | class LoggerAdapter(object): |
---|
1589 | n/a | """ |
---|
1590 | n/a | An adapter for loggers which makes it easier to specify contextual |
---|
1591 | n/a | information in logging output. |
---|
1592 | n/a | """ |
---|
1593 | n/a | |
---|
1594 | n/a | def __init__(self, logger, extra): |
---|
1595 | n/a | """ |
---|
1596 | n/a | Initialize the adapter with a logger and a dict-like object which |
---|
1597 | n/a | provides contextual information. This constructor signature allows |
---|
1598 | n/a | easy stacking of LoggerAdapters, if so desired. |
---|
1599 | n/a | |
---|
1600 | n/a | You can effectively pass keyword arguments as shown in the |
---|
1601 | n/a | following example: |
---|
1602 | n/a | |
---|
1603 | n/a | adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2")) |
---|
1604 | n/a | """ |
---|
1605 | n/a | self.logger = logger |
---|
1606 | n/a | self.extra = extra |
---|
1607 | n/a | |
---|
1608 | n/a | def process(self, msg, kwargs): |
---|
1609 | n/a | """ |
---|
1610 | n/a | Process the logging message and keyword arguments passed in to |
---|
1611 | n/a | a logging call to insert contextual information. You can either |
---|
1612 | n/a | manipulate the message itself, the keyword args or both. Return |
---|
1613 | n/a | the message and kwargs modified (or not) to suit your needs. |
---|
1614 | n/a | |
---|
1615 | n/a | Normally, you'll only need to override this one method in a |
---|
1616 | n/a | LoggerAdapter subclass for your specific needs. |
---|
1617 | n/a | """ |
---|
1618 | n/a | kwargs["extra"] = self.extra |
---|
1619 | n/a | return msg, kwargs |
---|
1620 | n/a | |
---|
1621 | n/a | # |
---|
1622 | n/a | # Boilerplate convenience methods |
---|
1623 | n/a | # |
---|
1624 | n/a | def debug(self, msg, *args, **kwargs): |
---|
1625 | n/a | """ |
---|
1626 | n/a | Delegate a debug call to the underlying logger. |
---|
1627 | n/a | """ |
---|
1628 | n/a | self.log(DEBUG, msg, *args, **kwargs) |
---|
1629 | n/a | |
---|
1630 | n/a | def info(self, msg, *args, **kwargs): |
---|
1631 | n/a | """ |
---|
1632 | n/a | Delegate an info call to the underlying logger. |
---|
1633 | n/a | """ |
---|
1634 | n/a | self.log(INFO, msg, *args, **kwargs) |
---|
1635 | n/a | |
---|
1636 | n/a | def warning(self, msg, *args, **kwargs): |
---|
1637 | n/a | """ |
---|
1638 | n/a | Delegate a warning call to the underlying logger. |
---|
1639 | n/a | """ |
---|
1640 | n/a | self.log(WARNING, msg, *args, **kwargs) |
---|
1641 | n/a | |
---|
1642 | n/a | def warn(self, msg, *args, **kwargs): |
---|
1643 | n/a | warnings.warn("The 'warn' method is deprecated, " |
---|
1644 | n/a | "use 'warning' instead", DeprecationWarning, 2) |
---|
1645 | n/a | self.warning(msg, *args, **kwargs) |
---|
1646 | n/a | |
---|
1647 | n/a | def error(self, msg, *args, **kwargs): |
---|
1648 | n/a | """ |
---|
1649 | n/a | Delegate an error call to the underlying logger. |
---|
1650 | n/a | """ |
---|
1651 | n/a | self.log(ERROR, msg, *args, **kwargs) |
---|
1652 | n/a | |
---|
1653 | n/a | def exception(self, msg, *args, exc_info=True, **kwargs): |
---|
1654 | n/a | """ |
---|
1655 | n/a | Delegate an exception call to the underlying logger. |
---|
1656 | n/a | """ |
---|
1657 | n/a | self.log(ERROR, msg, *args, exc_info=exc_info, **kwargs) |
---|
1658 | n/a | |
---|
1659 | n/a | def critical(self, msg, *args, **kwargs): |
---|
1660 | n/a | """ |
---|
1661 | n/a | Delegate a critical call to the underlying logger. |
---|
1662 | n/a | """ |
---|
1663 | n/a | self.log(CRITICAL, msg, *args, **kwargs) |
---|
1664 | n/a | |
---|
1665 | n/a | def log(self, level, msg, *args, **kwargs): |
---|
1666 | n/a | """ |
---|
1667 | n/a | Delegate a log call to the underlying logger, after adding |
---|
1668 | n/a | contextual information from this adapter instance. |
---|
1669 | n/a | """ |
---|
1670 | n/a | if self.isEnabledFor(level): |
---|
1671 | n/a | msg, kwargs = self.process(msg, kwargs) |
---|
1672 | n/a | self.logger._log(level, msg, args, **kwargs) |
---|
1673 | n/a | |
---|
1674 | n/a | def isEnabledFor(self, level): |
---|
1675 | n/a | """ |
---|
1676 | n/a | Is this logger enabled for level 'level'? |
---|
1677 | n/a | """ |
---|
1678 | n/a | if self.logger.manager.disable >= level: |
---|
1679 | n/a | return False |
---|
1680 | n/a | return level >= self.getEffectiveLevel() |
---|
1681 | n/a | |
---|
1682 | n/a | def setLevel(self, level): |
---|
1683 | n/a | """ |
---|
1684 | n/a | Set the specified level on the underlying logger. |
---|
1685 | n/a | """ |
---|
1686 | n/a | self.logger.setLevel(level) |
---|
1687 | n/a | |
---|
1688 | n/a | def getEffectiveLevel(self): |
---|
1689 | n/a | """ |
---|
1690 | n/a | Get the effective level for the underlying logger. |
---|
1691 | n/a | """ |
---|
1692 | n/a | return self.logger.getEffectiveLevel() |
---|
1693 | n/a | |
---|
1694 | n/a | def hasHandlers(self): |
---|
1695 | n/a | """ |
---|
1696 | n/a | See if the underlying logger has any handlers. |
---|
1697 | n/a | """ |
---|
1698 | n/a | return self.logger.hasHandlers() |
---|
1699 | n/a | |
---|
1700 | n/a | def __repr__(self): |
---|
1701 | n/a | logger = self.logger |
---|
1702 | n/a | level = getLevelName(logger.getEffectiveLevel()) |
---|
1703 | n/a | return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level) |
---|
1704 | n/a | |
---|
1705 | n/a | root = RootLogger(WARNING) |
---|
1706 | n/a | Logger.root = root |
---|
1707 | n/a | Logger.manager = Manager(Logger.root) |
---|
1708 | n/a | |
---|
1709 | n/a | #--------------------------------------------------------------------------- |
---|
1710 | n/a | # Configuration classes and functions |
---|
1711 | n/a | #--------------------------------------------------------------------------- |
---|
1712 | n/a | |
---|
1713 | n/a | def basicConfig(**kwargs): |
---|
1714 | n/a | """ |
---|
1715 | n/a | Do basic configuration for the logging system. |
---|
1716 | n/a | |
---|
1717 | n/a | This function does nothing if the root logger already has handlers |
---|
1718 | n/a | configured. It is a convenience method intended for use by simple scripts |
---|
1719 | n/a | to do one-shot configuration of the logging package. |
---|
1720 | n/a | |
---|
1721 | n/a | The default behaviour is to create a StreamHandler which writes to |
---|
1722 | n/a | sys.stderr, set a formatter using the BASIC_FORMAT format string, and |
---|
1723 | n/a | add the handler to the root logger. |
---|
1724 | n/a | |
---|
1725 | n/a | A number of optional keyword arguments may be specified, which can alter |
---|
1726 | n/a | the default behaviour. |
---|
1727 | n/a | |
---|
1728 | n/a | filename Specifies that a FileHandler be created, using the specified |
---|
1729 | n/a | filename, rather than a StreamHandler. |
---|
1730 | n/a | filemode Specifies the mode to open the file, if filename is specified |
---|
1731 | n/a | (if filemode is unspecified, it defaults to 'a'). |
---|
1732 | n/a | format Use the specified format string for the handler. |
---|
1733 | n/a | datefmt Use the specified date/time format. |
---|
1734 | n/a | style If a format string is specified, use this to specify the |
---|
1735 | n/a | type of format string (possible values '%', '{', '$', for |
---|
1736 | n/a | %-formatting, :meth:`str.format` and :class:`string.Template` |
---|
1737 | n/a | - defaults to '%'). |
---|
1738 | n/a | level Set the root logger level to the specified level. |
---|
1739 | n/a | stream Use the specified stream to initialize the StreamHandler. Note |
---|
1740 | n/a | that this argument is incompatible with 'filename' - if both |
---|
1741 | n/a | are present, 'stream' is ignored. |
---|
1742 | n/a | handlers If specified, this should be an iterable of already created |
---|
1743 | n/a | handlers, which will be added to the root handler. Any handler |
---|
1744 | n/a | in the list which does not have a formatter assigned will be |
---|
1745 | n/a | assigned the formatter created in this function. |
---|
1746 | n/a | |
---|
1747 | n/a | Note that you could specify a stream created using open(filename, mode) |
---|
1748 | n/a | rather than passing the filename and mode in. However, it should be |
---|
1749 | n/a | remembered that StreamHandler does not close its stream (since it may be |
---|
1750 | n/a | using sys.stdout or sys.stderr), whereas FileHandler closes its stream |
---|
1751 | n/a | when the handler is closed. |
---|
1752 | n/a | |
---|
1753 | n/a | .. versionchanged:: 3.2 |
---|
1754 | n/a | Added the ``style`` parameter. |
---|
1755 | n/a | |
---|
1756 | n/a | .. versionchanged:: 3.3 |
---|
1757 | n/a | Added the ``handlers`` parameter. A ``ValueError`` is now thrown for |
---|
1758 | n/a | incompatible arguments (e.g. ``handlers`` specified together with |
---|
1759 | n/a | ``filename``/``filemode``, or ``filename``/``filemode`` specified |
---|
1760 | n/a | together with ``stream``, or ``handlers`` specified together with |
---|
1761 | n/a | ``stream``. |
---|
1762 | n/a | """ |
---|
1763 | n/a | # Add thread safety in case someone mistakenly calls |
---|
1764 | n/a | # basicConfig() from multiple threads |
---|
1765 | n/a | _acquireLock() |
---|
1766 | n/a | try: |
---|
1767 | n/a | if len(root.handlers) == 0: |
---|
1768 | n/a | handlers = kwargs.pop("handlers", None) |
---|
1769 | n/a | if handlers is None: |
---|
1770 | n/a | if "stream" in kwargs and "filename" in kwargs: |
---|
1771 | n/a | raise ValueError("'stream' and 'filename' should not be " |
---|
1772 | n/a | "specified together") |
---|
1773 | n/a | else: |
---|
1774 | n/a | if "stream" in kwargs or "filename" in kwargs: |
---|
1775 | n/a | raise ValueError("'stream' or 'filename' should not be " |
---|
1776 | n/a | "specified together with 'handlers'") |
---|
1777 | n/a | if handlers is None: |
---|
1778 | n/a | filename = kwargs.pop("filename", None) |
---|
1779 | n/a | mode = kwargs.pop("filemode", 'a') |
---|
1780 | n/a | if filename: |
---|
1781 | n/a | h = FileHandler(filename, mode) |
---|
1782 | n/a | else: |
---|
1783 | n/a | stream = kwargs.pop("stream", None) |
---|
1784 | n/a | h = StreamHandler(stream) |
---|
1785 | n/a | handlers = [h] |
---|
1786 | n/a | dfs = kwargs.pop("datefmt", None) |
---|
1787 | n/a | style = kwargs.pop("style", '%') |
---|
1788 | n/a | if style not in _STYLES: |
---|
1789 | n/a | raise ValueError('Style must be one of: %s' % ','.join( |
---|
1790 | n/a | _STYLES.keys())) |
---|
1791 | n/a | fs = kwargs.pop("format", _STYLES[style][1]) |
---|
1792 | n/a | fmt = Formatter(fs, dfs, style) |
---|
1793 | n/a | for h in handlers: |
---|
1794 | n/a | if h.formatter is None: |
---|
1795 | n/a | h.setFormatter(fmt) |
---|
1796 | n/a | root.addHandler(h) |
---|
1797 | n/a | level = kwargs.pop("level", None) |
---|
1798 | n/a | if level is not None: |
---|
1799 | n/a | root.setLevel(level) |
---|
1800 | n/a | if kwargs: |
---|
1801 | n/a | keys = ', '.join(kwargs.keys()) |
---|
1802 | n/a | raise ValueError('Unrecognised argument(s): %s' % keys) |
---|
1803 | n/a | finally: |
---|
1804 | n/a | _releaseLock() |
---|
1805 | n/a | |
---|
1806 | n/a | #--------------------------------------------------------------------------- |
---|
1807 | n/a | # Utility functions at module level. |
---|
1808 | n/a | # Basically delegate everything to the root logger. |
---|
1809 | n/a | #--------------------------------------------------------------------------- |
---|
1810 | n/a | |
---|
1811 | n/a | def getLogger(name=None): |
---|
1812 | n/a | """ |
---|
1813 | n/a | Return a logger with the specified name, creating it if necessary. |
---|
1814 | n/a | |
---|
1815 | n/a | If no name is specified, return the root logger. |
---|
1816 | n/a | """ |
---|
1817 | n/a | if name: |
---|
1818 | n/a | return Logger.manager.getLogger(name) |
---|
1819 | n/a | else: |
---|
1820 | n/a | return root |
---|
1821 | n/a | |
---|
1822 | n/a | def critical(msg, *args, **kwargs): |
---|
1823 | n/a | """ |
---|
1824 | n/a | Log a message with severity 'CRITICAL' on the root logger. If the logger |
---|
1825 | n/a | has no handlers, call basicConfig() to add a console handler with a |
---|
1826 | n/a | pre-defined format. |
---|
1827 | n/a | """ |
---|
1828 | n/a | if len(root.handlers) == 0: |
---|
1829 | n/a | basicConfig() |
---|
1830 | n/a | root.critical(msg, *args, **kwargs) |
---|
1831 | n/a | |
---|
1832 | n/a | fatal = critical |
---|
1833 | n/a | |
---|
1834 | n/a | def error(msg, *args, **kwargs): |
---|
1835 | n/a | """ |
---|
1836 | n/a | Log a message with severity 'ERROR' on the root logger. If the logger has |
---|
1837 | n/a | no handlers, call basicConfig() to add a console handler with a pre-defined |
---|
1838 | n/a | format. |
---|
1839 | n/a | """ |
---|
1840 | n/a | if len(root.handlers) == 0: |
---|
1841 | n/a | basicConfig() |
---|
1842 | n/a | root.error(msg, *args, **kwargs) |
---|
1843 | n/a | |
---|
1844 | n/a | def exception(msg, *args, exc_info=True, **kwargs): |
---|
1845 | n/a | """ |
---|
1846 | n/a | Log a message with severity 'ERROR' on the root logger, with exception |
---|
1847 | n/a | information. If the logger has no handlers, basicConfig() is called to add |
---|
1848 | n/a | a console handler with a pre-defined format. |
---|
1849 | n/a | """ |
---|
1850 | n/a | error(msg, *args, exc_info=exc_info, **kwargs) |
---|
1851 | n/a | |
---|
1852 | n/a | def warning(msg, *args, **kwargs): |
---|
1853 | n/a | """ |
---|
1854 | n/a | Log a message with severity 'WARNING' on the root logger. If the logger has |
---|
1855 | n/a | no handlers, call basicConfig() to add a console handler with a pre-defined |
---|
1856 | n/a | format. |
---|
1857 | n/a | """ |
---|
1858 | n/a | if len(root.handlers) == 0: |
---|
1859 | n/a | basicConfig() |
---|
1860 | n/a | root.warning(msg, *args, **kwargs) |
---|
1861 | n/a | |
---|
1862 | n/a | def warn(msg, *args, **kwargs): |
---|
1863 | n/a | warnings.warn("The 'warn' function is deprecated, " |
---|
1864 | n/a | "use 'warning' instead", DeprecationWarning, 2) |
---|
1865 | n/a | warning(msg, *args, **kwargs) |
---|
1866 | n/a | |
---|
1867 | n/a | def info(msg, *args, **kwargs): |
---|
1868 | n/a | """ |
---|
1869 | n/a | Log a message with severity 'INFO' on the root logger. If the logger has |
---|
1870 | n/a | no handlers, call basicConfig() to add a console handler with a pre-defined |
---|
1871 | n/a | format. |
---|
1872 | n/a | """ |
---|
1873 | n/a | if len(root.handlers) == 0: |
---|
1874 | n/a | basicConfig() |
---|
1875 | n/a | root.info(msg, *args, **kwargs) |
---|
1876 | n/a | |
---|
1877 | n/a | def debug(msg, *args, **kwargs): |
---|
1878 | n/a | """ |
---|
1879 | n/a | Log a message with severity 'DEBUG' on the root logger. If the logger has |
---|
1880 | n/a | no handlers, call basicConfig() to add a console handler with a pre-defined |
---|
1881 | n/a | format. |
---|
1882 | n/a | """ |
---|
1883 | n/a | if len(root.handlers) == 0: |
---|
1884 | n/a | basicConfig() |
---|
1885 | n/a | root.debug(msg, *args, **kwargs) |
---|
1886 | n/a | |
---|
1887 | n/a | def log(level, msg, *args, **kwargs): |
---|
1888 | n/a | """ |
---|
1889 | n/a | Log 'msg % args' with the integer severity 'level' on the root logger. If |
---|
1890 | n/a | the logger has no handlers, call basicConfig() to add a console handler |
---|
1891 | n/a | with a pre-defined format. |
---|
1892 | n/a | """ |
---|
1893 | n/a | if len(root.handlers) == 0: |
---|
1894 | n/a | basicConfig() |
---|
1895 | n/a | root.log(level, msg, *args, **kwargs) |
---|
1896 | n/a | |
---|
1897 | n/a | def disable(level=CRITICAL): |
---|
1898 | n/a | """ |
---|
1899 | n/a | Disable all logging calls of severity 'level' and below. |
---|
1900 | n/a | """ |
---|
1901 | n/a | root.manager.disable = level |
---|
1902 | n/a | |
---|
1903 | n/a | def shutdown(handlerList=_handlerList): |
---|
1904 | n/a | """ |
---|
1905 | n/a | Perform any cleanup actions in the logging system (e.g. flushing |
---|
1906 | n/a | buffers). |
---|
1907 | n/a | |
---|
1908 | n/a | Should be called at application exit. |
---|
1909 | n/a | """ |
---|
1910 | n/a | for wr in reversed(handlerList[:]): |
---|
1911 | n/a | #errors might occur, for example, if files are locked |
---|
1912 | n/a | #we just ignore them if raiseExceptions is not set |
---|
1913 | n/a | try: |
---|
1914 | n/a | h = wr() |
---|
1915 | n/a | if h: |
---|
1916 | n/a | try: |
---|
1917 | n/a | h.acquire() |
---|
1918 | n/a | h.flush() |
---|
1919 | n/a | h.close() |
---|
1920 | n/a | except (OSError, ValueError): |
---|
1921 | n/a | # Ignore errors which might be caused |
---|
1922 | n/a | # because handlers have been closed but |
---|
1923 | n/a | # references to them are still around at |
---|
1924 | n/a | # application exit. |
---|
1925 | n/a | pass |
---|
1926 | n/a | finally: |
---|
1927 | n/a | h.release() |
---|
1928 | n/a | except: # ignore everything, as we're shutting down |
---|
1929 | n/a | if raiseExceptions: |
---|
1930 | n/a | raise |
---|
1931 | n/a | #else, swallow |
---|
1932 | n/a | |
---|
1933 | n/a | #Let's try and shutdown automatically on application exit... |
---|
1934 | n/a | import atexit |
---|
1935 | n/a | atexit.register(shutdown) |
---|
1936 | n/a | |
---|
1937 | n/a | # Null handler |
---|
1938 | n/a | |
---|
1939 | n/a | class NullHandler(Handler): |
---|
1940 | n/a | """ |
---|
1941 | n/a | This handler does nothing. It's intended to be used to avoid the |
---|
1942 | n/a | "No handlers could be found for logger XXX" one-off warning. This is |
---|
1943 | n/a | important for library code, which may contain code to log events. If a user |
---|
1944 | n/a | of the library does not configure logging, the one-off warning might be |
---|
1945 | n/a | produced; to avoid this, the library developer simply needs to instantiate |
---|
1946 | n/a | a NullHandler and add it to the top-level logger of the library module or |
---|
1947 | n/a | package. |
---|
1948 | n/a | """ |
---|
1949 | n/a | def handle(self, record): |
---|
1950 | n/a | """Stub.""" |
---|
1951 | n/a | |
---|
1952 | n/a | def emit(self, record): |
---|
1953 | n/a | """Stub.""" |
---|
1954 | n/a | |
---|
1955 | n/a | def createLock(self): |
---|
1956 | n/a | self.lock = None |
---|
1957 | n/a | |
---|
1958 | n/a | # Warnings integration |
---|
1959 | n/a | |
---|
1960 | n/a | _warnings_showwarning = None |
---|
1961 | n/a | |
---|
1962 | n/a | def _showwarning(message, category, filename, lineno, file=None, line=None): |
---|
1963 | n/a | """ |
---|
1964 | n/a | Implementation of showwarnings which redirects to logging, which will first |
---|
1965 | n/a | check to see if the file parameter is None. If a file is specified, it will |
---|
1966 | n/a | delegate to the original warnings implementation of showwarning. Otherwise, |
---|
1967 | n/a | it will call warnings.formatwarning and will log the resulting string to a |
---|
1968 | n/a | warnings logger named "py.warnings" with level logging.WARNING. |
---|
1969 | n/a | """ |
---|
1970 | n/a | if file is not None: |
---|
1971 | n/a | if _warnings_showwarning is not None: |
---|
1972 | n/a | _warnings_showwarning(message, category, filename, lineno, file, line) |
---|
1973 | n/a | else: |
---|
1974 | n/a | s = warnings.formatwarning(message, category, filename, lineno, line) |
---|
1975 | n/a | logger = getLogger("py.warnings") |
---|
1976 | n/a | if not logger.handlers: |
---|
1977 | n/a | logger.addHandler(NullHandler()) |
---|
1978 | n/a | logger.warning("%s", s) |
---|
1979 | n/a | |
---|
1980 | n/a | def captureWarnings(capture): |
---|
1981 | n/a | """ |
---|
1982 | n/a | If capture is true, redirect all warnings to the logging package. |
---|
1983 | n/a | If capture is False, ensure that warnings are not redirected to logging |
---|
1984 | n/a | but to their original destinations. |
---|
1985 | n/a | """ |
---|
1986 | n/a | global _warnings_showwarning |
---|
1987 | n/a | if capture: |
---|
1988 | n/a | if _warnings_showwarning is None: |
---|
1989 | n/a | _warnings_showwarning = warnings.showwarning |
---|
1990 | n/a | warnings.showwarning = _showwarning |
---|
1991 | n/a | else: |
---|
1992 | n/a | if _warnings_showwarning is not None: |
---|
1993 | n/a | warnings.showwarning = _warnings_showwarning |
---|
1994 | n/a | _warnings_showwarning = None |
---|