# -*- coding: utf-8 -*- # vim: sw=4:ts=4:expandtab """ pygogo.utils ~~~~~~~~~~~~ Misc classes and functions that don't warrant their own module Examples: basic usage:: >>> CustomEncoder().encode(range(5)) '[0, 1, 2, 3, 4]' """ import logging import sys from json import JSONEncoder module_hdlr = logging.StreamHandler(sys.stdout) module_logger = logging.getLogger(__name__) module_logger.addHandler(module_hdlr) class CustomEncoder(JSONEncoder): """A unicode aware JSON encoder that can handle iterators, dates, and times Examples: >>> CustomEncoder().encode(range(5)) '[0, 1, 2, 3, 4]' >>> from json import dumps >>> dumps(range(5), cls=CustomEncoder) '[0, 1, 2, 3, 4]' """ def default(self, obj): """ Encodes a given object Args: obj (scalar): The object to encode. Returns: The encoded object Examples: >>> CustomEncoder().default(range(5)) [0, 1, 2, 3, 4] """ if hasattr(obj, "real"): encoded = float(obj) elif hasattr(obj, "union"): encoded = tuple(obj) elif set(["next", "union", "__iter__"]).intersection(dir(obj)): encoded = list(obj) else: encoded = str(obj) return encoded class StructuredMessage(object): """Converts a message and kwargs to a json string Attributes: kwargs (dict): Keyword arguments passed to :class:`~pygogo.utils.CustomEncoder`. Args: message (string): The message to log. kwargs (dict): Keyword arguments passed to :class:`~pygogo.utils.CustomEncoder`. Returns: New instance of :class:`StructuredMessage` See also: :class:`pygogo.utils.StructuredAdapter` Examples: >>> from json import loads >>> msg = StructuredMessage('hello world', key='value') >>> loads(str(msg)) == {'message': 'hello world', 'key': 'value'} True """ def __init__(self, message=None, **kwargs): """Initialization method. Args: message (string): The message to log. kwargs (dict): Keyword arguments passed to :class:`~pygogo.utils.CustomEncoder`. Returns: New instance of :class:`StructuredMessage` Examples: >>> StructuredMessage('message') # doctest: +ELLIPSIS <pygogo.utils.StructuredMessage object at 0x...> """ kwargs["message"] = message self.kwargs = kwargs def __str__(self): """ String method Returns: str: The encoded object Examples >>> from json import loads >>> msg = str(StructuredMessage('hello world', key='value')) >>> loads(msg) == {'message': 'hello world', 'key': 'value'} True """ return str(CustomEncoder().encode(self.kwargs)) class StructuredAdapter(logging.LoggerAdapter): """A logging adapter that creates a json string from a log message and the `extra` kwarg See also: :class:`pygogo.utils.StructuredMessage` :meth:`pygogo.Gogo.get_structured_logger` Examples: >>> from io import StringIO >>> from json import loads >>> s = StringIO() >>> logger = logging.getLogger() >>> hdlr = logging.StreamHandler(s) >>> logger.addHandler(hdlr) >>> structured_logger = StructuredAdapter(logger, {'all': True}) >>> structured_logger.debug('hello', extra={'key': u'value'}) >>> loads(s.getvalue()) == { ... 'all': True, 'message': 'hello', 'key': 'value'} True """ def process(self, msg, kwargs): """ Modifies the message and/or keyword arguments passed to a logging call in order to insert contextual information. Args: msg (str): The message to log. kwargs (dict): Returns: Tuple of (:class:`~pygogo.utils.StructuredMessage`, modified kwargs) Examples: >>> from json import loads >>> logger = logging.getLogger() >>> structured_logger = StructuredAdapter(logger, {'all': True}) >>> extra = {'key': 'value'} >>> m, k = structured_logger.process('message', {'extra': extra}) >>> loads(m) == {'all': True, 'message': 'message', 'key': 'value'} True >>> k == {'extra': {'all': True, 'key': 'value'}} True """ extra = kwargs.get("extra", {}) extra.update(self.extra) kwargs["extra"] = extra return str(StructuredMessage(msg, **extra)), kwargs class LogFilter(logging.Filter): """Filters log messages depending on level Attributes: level (int): The logging level. +-------------------------+-------+ | logging level attribute | value | +=========================+=======+ | CRITICAL | 50 | +-------------------------+-------+ | ERROR | 40 | +-------------------------+-------+ | WARNING | 30 | +-------------------------+-------+ | INFO | 20 | +-------------------------+-------+ | DEBUG | 10 | +-------------------------+-------+ | NOTSET | 0 | +-------------------------+-------+ Args: level (int): The logging level. Returns: New instance of :class:`LogFilter` See also: :meth:`pygogo.Gogo.update_hdlr` """ def __init__(self, level): """Initialization method. Args: level (int): The logging level. Returns: New instance of :class:`LogFilter` Examples: >>> LogFilter(40) # doctest: +ELLIPSIS <pygogo.utils.LogFilter object at 0x...> """ self.high_level = level def filter(self, record): """Determines whether or a not a message should be logged. Args: record (obj): The event to (potentially) log Returns: bool: True if the event level is lower than self.high_level Examples: >>> attrs = {'levelno': logging.INFO} >>> record = logging.makeLogRecord(attrs) >>> LogFilter(40).filter(record) True """ return record.levelno < self.high_level def get_structured_filter(name="", **kwargs): """Returns a structured filter that injects contextual information into log records. Args: kwargs (dict): The contextual information you wish to inject See also: :meth:`pygogo.Gogo.update_hdlr` Returns: New instance of :class:`pygogo.utils.StructuredFilter` Examples: >>> structured_filter = get_structured_filter(user='fred') >>> structured_filter # doctest: +ELLIPSIS <pygogo.utils...StructuredFilter object at 0x...> >>> >>> logger = logging.getLogger('structured_filter') >>> hdlr = logging.StreamHandler(sys.stdout) >>> formatter = logging.Formatter('User %(user)s said, "%(message)s".') >>> hdlr.setFormatter(formatter) >>> logger.addFilter(structured_filter) >>> logger.addHandler(hdlr) >>> logger.debug('A debug message') User fred said, "A debug message". """ class StructuredFilter(logging.Filter): """ Injects contextual information into log records. """ def filter(self, record): """Adds contextual information to a log record Args: record (obj): The event to contextualize Returns: bool: True """ for k, v in kwargs.items(): setattr(record, k, v) return True return StructuredFilter(name)