From 77a45c6970a03d0ea13bf104fc0f9d7e22453b88 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 15 Apr 2014 14:24:53 +0100 Subject: Closes #21203: Updated fileConfig and dictConfig to remove inconsistencies. Thanks to Jure Koren for the patch. --- Lib/logging/config.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 895fb263c0..8a99923bf3 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -116,11 +116,12 @@ def _create_formatters(cp): sectname = "formatter_%s" % form fs = cp.get(sectname, "format", raw=True, fallback=None) dfs = cp.get(sectname, "datefmt", raw=True, fallback=None) + stl = cp.get(sectname, "style", raw=True, fallback='%') c = logging.Formatter class_name = cp[sectname].get("class") if class_name: c = _resolve(class_name) - f = c(fs, dfs) + f = c(fs, dfs, stl) formatters[form] = f return formatters @@ -660,7 +661,12 @@ class DictConfigurator(BaseConfigurator): fmt = config.get('format', None) dfmt = config.get('datefmt', None) style = config.get('style', '%') - result = logging.Formatter(fmt, dfmt, style) + cname = config.get('class', None) + if not cname: + c = logging.Formatter + else: + c = _resolve(cname) + result = c(fmt, dfmt, style) return result def configure_filter(self, config): -- cgit v1.2.1 From 41610c48e18673c510c5baa28566e562a93063a1 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sun, 14 Sep 2014 21:29:11 +0100 Subject: Closes #20537: logging methods now accept an exception instance as well as a Boolean value or exception tuple. Thanks to Yury Selivanov for the patch. --- Lib/logging/__init__.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 7fb3a352e5..7628d84b43 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1302,12 +1302,11 @@ class Logger(Filterer): if self.isEnabledFor(ERROR): self._log(ERROR, msg, args, **kwargs) - def exception(self, msg, *args, **kwargs): + def exception(self, msg, *args, exc_info=True, **kwargs): """ Convenience method for logging an ERROR with exception information. """ - kwargs['exc_info'] = True - self.error(msg, *args, **kwargs) + self.error(msg, *args, exc_info=exc_info, **kwargs) def critical(self, msg, *args, **kwargs): """ @@ -1402,7 +1401,9 @@ class Logger(Filterer): else: # pragma: no cover fn, lno, func = "(unknown file)", 0, "(unknown function)" if exc_info: - if not isinstance(exc_info, tuple): + if isinstance(exc_info, BaseException): + exc_info = (type(exc_info), exc_info, exc_info.__traceback__) + elif not isinstance(exc_info, tuple): exc_info = sys.exc_info() record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra, sinfo) @@ -1612,12 +1613,11 @@ class LoggerAdapter(object): """ self.log(ERROR, msg, *args, **kwargs) - def exception(self, msg, *args, **kwargs): + def exception(self, msg, *args, exc_info=True, **kwargs): """ Delegate an exception call to the underlying logger. """ - kwargs["exc_info"] = True - self.log(ERROR, msg, *args, **kwargs) + self.log(ERROR, msg, *args, exc_info=exc_info, **kwargs) def critical(self, msg, *args, **kwargs): """ @@ -1796,14 +1796,13 @@ def error(msg, *args, **kwargs): basicConfig() root.error(msg, *args, **kwargs) -def exception(msg, *args, **kwargs): +def exception(msg, *args, exc_info=True, **kwargs): """ Log a message with severity 'ERROR' on the root logger, with exception information. If the logger has no handlers, basicConfig() is called to add a console handler with a pre-defined format. """ - kwargs['exc_info'] = True - error(msg, *args, **kwargs) + error(msg, *args, exc_info=exc_info, **kwargs) def warning(msg, *args, **kwargs): """ -- cgit v1.2.1 From 27d14faba6adcf77111c8e82c1c755ab7a7a8796 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 6 Jan 2015 10:56:09 +0000 Subject: Closes #23151: Removed unnecessary initialization. --- Lib/logging/__init__.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 7628d84b43..37132d909f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1086,7 +1086,6 @@ class PlaceHolder(object): # # Determine which class to use when instantiating loggers. # -_loggerClass = None def setLoggerClass(klass): """ @@ -1105,7 +1104,6 @@ def getLoggerClass(): """ Return the class to be used when instantiating a logger. """ - return _loggerClass class Manager(object): -- cgit v1.2.1 From a2b0f7fe3caae2837ca39865c3033789224aea8a Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 6 Jan 2015 11:19:42 +0000 Subject: Closes #21980: Added a __repr__ for LogRecord. --- Lib/logging/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 37132d909f..5113966a5e 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2001-2014 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Logging package for Python. Based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -316,6 +316,8 @@ class LogRecord(object): return ''%(self.name, self.levelno, self.pathname, self.lineno, self.msg) + __repr__ = __str__ + def getMessage(self): """ Return the message for this LogRecord. -- cgit v1.2.1 From 09efffa504452ade913b0c5847afd50b6a74d6cd Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 9 Feb 2015 19:49:00 +0000 Subject: Added respect_handler_level to QueueListener. --- Lib/logging/handlers.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index c67ac99cbf..d4f8aef6e7 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1,4 +1,4 @@ -# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Additional handlers for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. To use, simply 'import logging.handlers' and log away! """ @@ -1350,7 +1350,7 @@ if threading: """ _sentinel = None - def __init__(self, queue, *handlers): + def __init__(self, queue, *handlers, respect_handler_level=False): """ Initialise an instance with the specified queue and handlers. @@ -1359,6 +1359,7 @@ if threading: self.handlers = handlers self._stop = threading.Event() self._thread = None + self.respect_handler_level = respect_handler_level def dequeue(self, block): """ @@ -1399,7 +1400,12 @@ if threading: """ record = self.prepare(record) for handler in self.handlers: - handler.handle(record) + if not self.respect_handler_level: + process = True + else: + process = record.levelno >= handler.level + if process: + handler.handle(record) def _monitor(self): """ -- cgit v1.2.1 From 9c8cbea8d118fe73b5233376b1acb6c9ff981e95 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Thu, 1 Oct 2015 20:54:41 +0100 Subject: Closes #24884: refactored WatchedFileHandler file reopening into a separate method, based on a suggestion and patch by Marian Horban. --- Lib/logging/handlers.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 02a5fc1283..54bee893e1 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -440,11 +440,11 @@ class WatchedFileHandler(logging.FileHandler): sres = os.fstat(self.stream.fileno()) self.dev, self.ino = sres[ST_DEV], sres[ST_INO] - def emit(self, record): + def reopenIfNeeded(self): """ - Emit a record. + Reopen log file if needed. - First check if the underlying file has changed, and if it + Checks if the underlying file has changed, and if it has, close the old stream and reopen the file to get the current stream. """ @@ -467,6 +467,15 @@ class WatchedFileHandler(logging.FileHandler): # open a new file handle and get new stat info from that fd self.stream = self._open() self._statstream() + + def emit(self, record): + """ + Emit a record. + + If underlying file has changed, reopen the file before emitting the + record to it. + """ + self.reopenIfNeeded() logging.FileHandler.emit(self, record) -- cgit v1.2.1 From 47bfb98000c9ebaa9f64d9b9f80e80a456279c6b Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 14 Nov 2015 12:46:42 +0000 Subject: Issue #23883: Add missing APIs to __all__; patch by Jacek Ko?odziej --- Lib/logging/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 104b0be8d0..369d2c342a 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -33,8 +33,9 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR', 'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'basicConfig', 'captureWarnings', 'critical', 'debug', 'disable', 'error', 'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass', - 'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning', - 'getLogRecordFactory', 'setLogRecordFactory', 'lastResort'] + 'info', 'log', 'makeLogRecord', 'setLoggerClass', 'shutdown', + 'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory', + 'lastResort', 'raiseExceptions'] try: import threading -- cgit v1.2.1 From aae8c689e5313be4cf523ab9819ca36dabd62398 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 26 Dec 2015 12:48:44 +0000 Subject: Closes #25685: Made SocketHandler emission more efficient. --- Lib/logging/handlers.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Lib/logging') diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index b810fa9c58..c6840c30b3 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -588,6 +588,8 @@ class SocketHandler(logging.Handler): d['msg'] = record.getMessage() d['args'] = None d['exc_info'] = None + # Issue #25685: delete 'message' if present: redundant with 'msg' + d.pop('message', None) s = pickle.dumps(d, 1) slen = struct.pack(">L", len(s)) return slen + s -- cgit v1.2.1 From 954889890bb9ddd6666faaa56cfe122ec3024274 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 25 Feb 2016 20:17:45 +0100 Subject: Closes #26435: fix syntax in directives. Thanks to Jakub Stasiak. --- Lib/logging/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 104b0be8d0..a7bd890e3c 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -471,7 +471,7 @@ class Formatter(object): use one of %-formatting, :meth:`str.format` (``{}``) formatting or :class:`string.Template` formatting in your format string. - .. versionchanged: 3.2 + .. versionchanged:: 3.2 Added the ``style`` parameter. """ if style not in _STYLES: @@ -700,7 +700,7 @@ class Filterer(object): this and the record is then dropped. Returns a zero value if a record is to be dropped, else non-zero. - .. versionchanged: 3.2 + .. versionchanged:: 3.2 Allow filters to be just callables. """ -- cgit v1.2.1 From 9295250b0a4a41917c6ef3a6b32ff239391e24a7 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 22 Jul 2016 16:27:31 +0100 Subject: Closes #26559: Allow configuring flush-on-close behaviour of MemoryHandler. --- Lib/logging/handlers.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index c0748a8853..296d6cfa30 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1,4 +1,4 @@ -# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Additional handlers for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. To use, simply 'import logging.handlers' and log away! """ @@ -1238,17 +1238,25 @@ class MemoryHandler(BufferingHandler): flushing them to a target handler. Flushing occurs whenever the buffer is full, or when an event of a certain severity or greater is seen. """ - def __init__(self, capacity, flushLevel=logging.ERROR, target=None): + def __init__(self, capacity, flushLevel=logging.ERROR, target=None, + flushOnClose=True): """ Initialize the handler with the buffer size, the level at which flushing should occur and an optional target. Note that without a target being set either here or via setTarget(), a MemoryHandler is no use to anyone! + + The ``flushOnClose`` argument is ``True`` for backward compatibility + reasons - the old behaviour is that when the handler is closed, the + buffer is flushed, even if the flush level hasn't been exceeded nor the + capacity exceeded. To prevent this, set ``flushOnClose`` to ``False``. """ BufferingHandler.__init__(self, capacity) self.flushLevel = flushLevel self.target = target + # See Issue #26559 for why this has been added + self.flushOnClose = flushOnClose def shouldFlush(self, record): """ @@ -1282,10 +1290,12 @@ class MemoryHandler(BufferingHandler): def close(self): """ - Flush, set the target to None and lose the buffer. + Flush, if appropriately configured, set the target to None and lose the + buffer. """ try: - self.flush() + if self.flushOnClose: + self.flush() finally: self.acquire() try: -- cgit v1.2.1 From bc72d8635aadf9302dd3bd72acdb6d0039ec5cc4 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 22 Jul 2016 18:23:04 +0100 Subject: Closes #27493: accepted Path objects in file handlers for logging. --- Lib/logging/__init__.py | 6 ++++-- Lib/logging/handlers.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index f941f4884b..fd422ea1e5 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Logging package for Python. Based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -994,6 +994,8 @@ class FileHandler(StreamHandler): """ Open the specified file and use it as the stream for logging. """ + # Issue #27493: add support for Path objects to be passed in + filename = os.fspath(filename) #keep the absolute path, otherwise derived classes which use this #may come a cropper when the current directory changes self.baseFilename = os.path.abspath(filename) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 296d6cfa30..ba00a69139 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -246,6 +246,9 @@ class TimedRotatingFileHandler(BaseRotatingHandler): self.extMatch = re.compile(self.extMatch, re.ASCII) self.interval = self.interval * interval # multiply by units requested + # The following line added because the filename passed in could be a + # path object (see Issue #27493), but self.baseFilename will be a string + filename = self.baseFilename if os.path.exists(filename): t = os.stat(filename)[ST_MTIME] else: -- cgit v1.2.1 From 29efc9ef2b969daa1e95a6b0008409fe045ac40f Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 6 Aug 2016 10:28:31 +0100 Subject: Closes #27650: Implemented repr methods for logging objects. --- Lib/logging/__init__.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index fd422ea1e5..4d872bd044 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -935,6 +935,10 @@ class Handler(Filterer): finally: del t, v, tb + def __repr__(self): + level = getLevelName(self.level) + return '<%s (%s)>' % (self.__class__.__name__, level) + class StreamHandler(Handler): """ A handler class which writes logging records, appropriately formatted, @@ -986,6 +990,14 @@ class StreamHandler(Handler): except Exception: self.handleError(record) + def __repr__(self): + level = getLevelName(self.level) + name = getattr(self.stream, 'name', '') + if name: + name += ' ' + return '<%s %s(%s)>' % (self.__class__.__name__, name, level) + + class FileHandler(StreamHandler): """ A handler class which writes formatted logging records to disk files. @@ -1050,6 +1062,11 @@ class FileHandler(StreamHandler): self.stream = self._open() StreamHandler.emit(self, record) + def __repr__(self): + level = getLevelName(self.level) + return '<%s %s (%s)>' % (self.__class__.__name__, self.baseFilename, level) + + class _StderrHandler(StreamHandler): """ This class is like a StreamHandler using sys.stderr, but always uses @@ -1542,6 +1559,11 @@ class Logger(Filterer): suffix = '.'.join((self.name, suffix)) return self.manager.getLogger(suffix) + def __repr__(self): + level = getLevelName(self.getEffectiveLevel()) + return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level) + + class RootLogger(Logger): """ A root logger is not that different to any other logger, except that @@ -1668,6 +1690,11 @@ class LoggerAdapter(object): """ return self.logger.hasHandlers() + def __repr__(self): + logger = self.logger + level = getLevelName(logger.getEffectiveLevel()) + return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level) + root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) -- cgit v1.2.1 From b0e37ae0088ddea4584a58696ba66416e5dc6e95 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 3 Sep 2016 15:56:07 +0100 Subject: Fixes #27937: optimise code used in all logging calls. --- Lib/logging/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 4d872bd044..0c5c2ec28f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -130,8 +130,9 @@ def getLevelName(level): Otherwise, the string "Level %s" % level is returned. """ - # See Issue #22386 for the reason for this convoluted expression - return _levelToName.get(level, _nameToLevel.get(level, ("Level %s" % level))) + # See Issues #22386 and #27937 for why it's this way + return (_levelToName.get(level) or _nameToLevel.get(level) or + "Level %s" % level) def addLevelName(level, levelName): """ -- cgit v1.2.1 From 3594ee1f37b406e07df3ebcbe97e19339c447fac Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 3 Sep 2016 17:04:36 +0100 Subject: Closes #27935: returned numeric value for 'FATAL' logging level. --- Lib/logging/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib/logging') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 0c5c2ec28f..2590d6528f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -108,6 +108,7 @@ _levelToName = { } _nameToLevel = { 'CRITICAL': CRITICAL, + 'FATAL': FATAL, 'ERROR': ERROR, 'WARN': WARNING, 'WARNING': WARNING, -- cgit v1.2.1