Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: a11644408b
Fetching contributors…

Cannot retrieve contributors at this time

152 lines (127 sloc) 5.574 kb
# Copyright 2012 Gregory Holt
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.
Provides logging utilities for brimd; normally you don't need to
use this module directly as the active logger itself is passed via
the WSGI ``env['brim.logger']`` value.
__all__ = ['get_logger', 'NOTICE', 'sysloggable_excinfo']
import logging
from import thread, threading
from logging import StreamHandler
from logging.handlers import SysLogHandler
from sys import exc_info, stdout
from traceback import format_exception
# Patch up logging to support Eventlet coroutines.
logging.thread = thread
logging.threading = threading
logging._lock = logging.threading.RLock()
#: An additional log level patched into the standard logging levels;
#: this level is to be used for logging HTTP requests only so that it
#: can be easily filtered on to form access logs.
logging._levelNames[NOTICE] = 'NOTICE'
SysLogHandler.priority_map['NOTICE'] = 'notice'
class _LogAdapter(logging.LoggerAdapter, object):
Support for the thread/coroutine-local ``txn`` attribute, passing
of the server name and txn attribute with each log record, and
providing an additional :py:func:`notice` method for the new
NOTICE log level.
_cls_thread_local = threading.local()
def __init__(self, logger, server):
logging.LoggerAdapter.__init__(self, logger, {})
self.server = server
def txn(self):
if hasattr(self._cls_thread_local, 'txn'):
return self._cls_thread_local.txn
def txn(self, value):
self._cls_thread_local.txn = value
def getEffectiveLevel(self):
return self.logger.getEffectiveLevel()
def process(self, msg, kwargs):
kwargs['extra'] = {'server': self.server, 'txn': self.txn}
return msg, kwargs
def exception(self, msg, *args, **kwargs):
self.error('%s %s' % (msg, sysloggable_excinfo()))
def notice(self, msg, *args, **kwargs):
self.log(NOTICE, msg, *args, **kwargs)
class _LogFormatter(logging.Formatter):
Formatter that always emits the server name and ensures the txn
(if set) is always in the log line somewhere.
def __init__(self):
logging.Formatter.__init__(self, '%(server)s %(message)s')
def format(self, record):
msg = logging.Formatter.format(self, record)
if record.txn and record.levelno != NOTICE and record.txn not in msg:
msg = '%s txn:%s' % (msg, record.txn)
return msg
def sysloggable_excinfo(*excinfo):
Returns exception information as a string suitable for sending to
syslog (no newlines, the exception type and message first in case
the line is truncated, etc.).
:param excinfo: The exception info (exctype, value, traceback)
such as returned with sys.exc_info.
:returns: A str represention suitable for syslog.
if not excinfo:
excinfo = exc_info()
if excinfo[0] == KeyboardInterrupt:
return 'KeyboardInterrupt'
lines = ''.join(format_exception(*excinfo)).strip().split('\n')
return '%s %r' % (lines[-1], lines)
def get_logger(route, name, level, facility, console):
Returns a logging.Logger based on the information given.
:param route: The str log route, which is often the same as the
name but does not have to be. Think of this as the
key for the logger in source code.
:param name: The str log name, this is the name sent to the
underlying log system. Think of this as the display
name for the logger.
:param level: The str log level for which any records at or above
the level will be sent to the underlying log
system. Any records below the level will be
:param facility: If the underlying log system supports it, such
as syslog, this str facility value can help
direct the system where to store the records.
:param console: If set True, the underlying log system will
simply be to sys.stdout. This can be useful for
debugging. Normally you'll want to set this False
so the log records are sent to syslog.
:returns: A configured logging.Logger.
logger = logging.getLogger(route)
logger.propagate = False
if not hasattr(get_logger, 'handler2logger'):
get_logger.handler2logger = {}
if logger in get_logger.handler2logger:
if console:
handler = StreamHandler(stdout)
facility = getattr(SysLogHandler, facility)
handler = SysLogHandler(address='/dev/log', facility=facility)
get_logger.handler2logger[logger] = handler
logger.setLevel(getattr(logging, level.upper()))
return _LogAdapter(logger, name)
Jump to Line
Something went wrong with that request. Please try again.