diff options
Diffstat (limited to 'logutils/__init__.py')
-rw-r--r-- | logutils/__init__.py | 186 |
1 files changed, 185 insertions, 1 deletions
diff --git a/logutils/__init__.py b/logutils/__init__.py index f57f517..6ec0e0b 100644 --- a/logutils/__init__.py +++ b/logutils/__init__.py @@ -1,2 +1,186 @@ -__version__ = '0.1' +""" +The logutils package provides a set of handlers for the Python standard +library's logging package. + +Some of these handlers are out-of-scope for the standard library, and +so they are packaged here. Others are updated versions which have +appeared in recent Python releases, but are usable with older versions +of Python, and so are packaged here. +""" +import logging +from string import Template + +__version__ = '0.2' + +class NullHandler(logging.Handler): + """ + This handler does nothing. It's intended to be used to avoid the + "No handlers could be found for logger XXX" one-off warning. This is + important for library code, which may contain code to log events. If a user + of the library does not configure logging, the one-off warning might be + produced; to avoid this, the library developer simply needs to instantiate + a NullHandler and add it to the top-level logger of the library module or + package. + """ + + def handle(self, record): + """ + Handle a record. Does nothing in this class, but in other + handlers it typically filters and then emits the record in a + thread-safe way. + """ + pass + + def emit(self, record): + """ + Emit a record. This does nothing and shouldn't be called during normal + processing, unless you redefine :meth:`~logutils.NullHandler.handle`. + """ + pass + + def createLock(self): + """ + Since this handler does nothing, it has no underlying I/O to protect + against multi-threaded access, so this method returns `None`. + """ + self.lock = None + +class PercentStyle(object): + + default_format = '%(message)s' + asctime_format = '%(asctime)s' + + def __init__(self, fmt): + self._fmt = fmt or self.default_format + + def usesTime(self): + return self._fmt.find(self.asctime_format) >= 0 + + def format(self, record): + return self._fmt % record.__dict__ + +class StrFormatStyle(PercentStyle): + default_format = '{message}' + asctime_format = '{asctime}' + + def format(self, record): + return self._fmt.format(**record.__dict__) + + +class StringTemplateStyle(PercentStyle): + default_format = '${message}' + asctime_format = '${asctime}' + + def __init__(self, fmt): + self._fmt = fmt or self.default_format + self._tpl = Template(self._fmt) + + def usesTime(self): + fmt = self._fmt + return fmt.find('$asctime') >= 0 or fmt.find(self.asctime_format) >= 0 + + def format(self, record): + return self._tpl.substitute(**record.__dict__) + +_STYLES = { + '%': PercentStyle, + '{': StrFormatStyle, + '$': StringTemplateStyle +} + +class Formatter(logging.Formatter): + """ + Subclasses Formatter in Pythons earlier than 3.2 in order to give + 3.2 Formatter behaviour with respect to allowing %-, {} or $- + formatting. + """ + def __init__(self, fmt=None, datefmt=None, style='%'): + """ + Initialize the formatter with specified format strings. + + Initialize the formatter either with the specified format string, or a + default as described above. Allow for specialized date formatting with + the optional datefmt argument (if omitted, you get the ISO8601 format). + + Use a style parameter of '%', '{' or '$' to specify that you want to + use one of %-formatting, :meth:`str.format` (``{}``) formatting or + :class:`string.Template` formatting in your format string. + """ + if style not in _STYLES: + raise ValueError('Style must be one of: %s' % ','.join( + _STYLES.keys())) + self._style = _STYLES[style](fmt) + self._fmt = self._style._fmt + self.datefmt = datefmt + + def usesTime(self): + """ + Check if the format uses the creation time of the record. + """ + return self._style.usesTime() + + def formatMessage(self, record): + return self._style.format(record) + + def format(self, record): + """ + Format the specified record as text. + + The record's attribute dictionary is used as the operand to a + string formatting operation which yields the returned string. + Before formatting the dictionary, a couple of preparatory steps + are carried out. The message attribute of the record is computed + using LogRecord.getMessage(). If the formatting string uses the + time (as determined by a call to usesTime(), formatTime() is + called to format the event time. If there is exception information, + it is formatted using formatException() and appended to the message. + """ + record.message = record.getMessage() + if self.usesTime(): + record.asctime = self.formatTime(record, self.datefmt) + s = self.formatMessage(record) + if record.exc_info: + # Cache the traceback text to avoid converting it multiple times + # (it's constant anyway) + if not record.exc_text: + record.exc_text = self.formatException(record.exc_info) + if record.exc_text: + if s[-1:] != "\n": + s = s + "\n" + s = s + record.exc_text + return s + + +class BraceMessage(object): + def __init__(self, fmt, *args, **kwargs): + self.fmt = fmt + self.args = args + self.kwargs = kwargs + + def __str__(self): + return self.fmt.format(*self.args, **self.kwargs) + +class DollarMessage(object): + def __init__(self, fmt, **kwargs): + self.fmt = fmt + self.kwargs = kwargs + + def __str__(self): + from string import Template + return Template(self.fmt).substitute(**self.kwargs) + +def hasHandlers(logger): + """ + See if a logger has any handlers. + """ + rv = False + while logger: + if logger.handlers: + rv = True + break + elif not logger.propagate: + break + else: + logger = logger.parent + return rv |