rollbar/logger.py
"""
Hooks for integrating with the python logging framework.
Usage:
import logging
from rollbar.logger import RollbarHandler
rollbar.init('ACCESS_TOKEN', 'ENVIRONMENT')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# report ERROR and above to Rollbar
rollbar_handler = RollbarHandler()
rollbar_handler.setLevel(logging.ERROR)
# attach the handlers to the root logger
logger.addHandler(rollbar_handler)
"""
import logging
import threading
from logging.config import ConvertingDict, ConvertingList, ConvertingTuple
import rollbar
# hack to fix backward compatibility in Python3
try:
from logging import _checkLevel
except ImportError:
_checkLevel = lambda lvl: lvl
def resolve_logging_types(obj):
if isinstance(obj, (dict, ConvertingDict)):
return {k: resolve_logging_types(v) for k, v in obj.items()}
elif isinstance(obj, (list, ConvertingList)):
return [resolve_logging_types(i) for i in obj]
elif isinstance(obj, (tuple, ConvertingTuple)):
return tuple(resolve_logging_types(i) for i in obj)
return obj
class RollbarHandler(logging.Handler):
SUPPORTED_LEVELS = set(('debug', 'info', 'warning', 'error', 'critical'))
_history = threading.local()
def __init__(self,
access_token=None,
environment=None,
level=logging.INFO,
history_size=10,
history_level=logging.DEBUG,
**kw):
logging.Handler.__init__(self)
if access_token is not None:
rollbar.init(
access_token, environment, **resolve_logging_types(kw))
self.notify_level = _checkLevel(level)
self.history_size = history_size
if history_size > 0:
self._history.records = []
self.setHistoryLevel(history_level)
def setLevel(self, level):
"""
Override so we set the effective level for which
log records we notify Rollbar about instead of which
records we save to the history.
"""
self.notify_level = _checkLevel(level)
def setHistoryLevel(self, level):
"""
Use this method to determine which records we record history
for. Use setLevel() to determine which level we report records
to Rollbar for.
"""
logging.Handler.setLevel(self, level)
def emit(self, record):
# If the record came from Rollbar's own logger don't report it
# to Rollbar
if record.name == rollbar.__log_name__:
return
level = record.levelname.lower()
if level not in self.SUPPORTED_LEVELS:
return
exc_info = record.exc_info
extra_data = {
'args': record.args,
'record': {
'created': record.created,
'funcName': record.funcName,
'lineno': record.lineno,
'module': record.module,
'name': record.name,
'pathname': record.pathname,
'process': record.process,
'processName': record.processName,
'relativeCreated': record.relativeCreated,
'thread': record.thread,
'threadName': record.threadName
}
}
extra_data.update(getattr(record, 'extra_data', {}))
payload_data = getattr(record, 'payload_data', {})
self._add_history(record, payload_data)
# after we've added the history data, check to see if the
# notify level is satisfied
if record.levelno < self.notify_level:
return
# Wait until we know we're going to send a report before trying to
# load the request
request = getattr(record, "request", None) or rollbar.get_request()
uuid = None
try:
# when not in an exception handler, exc_info == (None, None, None)
if exc_info and exc_info[0]:
if record.msg:
message_template = {
'body': {
'trace': {
'exception': {
'description': record.getMessage()
}
}
}
}
payload_data = rollbar.dict_merge(
payload_data, message_template, silence_errors=True)
uuid = rollbar.report_exc_info(exc_info,
level=level,
request=request,
extra_data=extra_data,
payload_data=payload_data)
else:
uuid = rollbar.report_message(record.getMessage(),
level=level,
request=request,
extra_data=extra_data,
payload_data=payload_data)
except:
self.handleError(record)
else:
if uuid:
record.rollbar_uuid = uuid
def _add_history(self, record, payload_data):
if hasattr(self._history, 'records'):
records = self._history.records
history = list(records[-self.history_size:])
if history:
history_data = [self._build_history_data(r) for r in history]
payload_data.setdefault('server', {})['history'] = history_data
records.append(record)
# prune the messages if we have too many
self._history.records = list(records[-self.history_size:])
def _build_history_data(self, record):
data = {'timestamp': record.created,
'format': record.msg,
'args': record.args}
if hasattr(record, 'rollbar_uuid'):
data['uuid'] = record.rollbar_uuid
return data