diff options
| author | Aymeric Augustin | 2013-11-24 10:42:43 +0100 |
|---|---|---|
| committer | Aymeric Augustin | 2013-11-24 11:01:44 +0100 |
| commit | fe3df822111d3b56000deca354b0bceed7bde9cc (patch) | |
| tree | 8fe21689037cd975f83e7f70480b96e0ba2e3b6a /debug_toolbar/panels/logging.py | |
| parent | 7d24008ac3d70796c1502215c665311d2f21d6fd (diff) | |
| download | django-debug-toolbar-fe3df822111d3b56000deca354b0bceed7bde9cc.tar.bz2 | |
Rename all panels consistently.
Enforce absolute imports to avoid clashing with built-in package names.
Thanks Jannis for his feedback.
Diffstat (limited to 'debug_toolbar/panels/logging.py')
| -rw-r--r-- | debug_toolbar/panels/logging.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/debug_toolbar/panels/logging.py b/debug_toolbar/panels/logging.py new file mode 100644 index 0000000..a37e000 --- /dev/null +++ b/debug_toolbar/panels/logging.py @@ -0,0 +1,139 @@ +from __future__ import absolute_import, unicode_literals + +import datetime +import logging +try: + import threading +except ImportError: + threading = None +from django.utils.translation import ungettext, ugettext_lazy as _ +from debug_toolbar.panels import Panel + +MESSAGE_IF_STRING_REPRESENTATION_INVALID = '[Could not get log message]' + + +class LogCollector(object): + def __init__(self): + if threading is None: + raise NotImplementedError( + "threading module is not available, " + "the logging panel cannot be used without it") + self.records = {} # a dictionary that maps threads to log records + + def add_record(self, record, thread=None): + # Avoid logging SQL queries since they are already in the SQL panel + # TODO: Make this check whether SQL panel is enabled + if record.get('channel', '') == 'django.db.backends': + return + + self.get_records(thread).append(record) + + def get_records(self, thread=None): + """ + Returns a list of records for the provided thread, of if none is provided, + returns a list for the current thread. + """ + if thread is None: + thread = threading.currentThread() + if thread not in self.records: + self.records[thread] = [] + return self.records[thread] + + def clear_records(self, thread=None): + if thread is None: + thread = threading.currentThread() + if thread in self.records: + del self.records[thread] + + +class ThreadTrackingHandler(logging.Handler): + def __init__(self, collector): + logging.Handler.__init__(self) + self.collector = collector + + def emit(self, record): + try: + message = record.getMessage() + except Exception: + message = MESSAGE_IF_STRING_REPRESENTATION_INVALID + + record = { + 'message': message, + 'time': datetime.datetime.fromtimestamp(record.created), + 'level': record.levelname, + 'file': record.pathname, + 'line': record.lineno, + 'channel': record.name, + } + self.collector.add_record(record) + + +collector = LogCollector() +logging_handler = ThreadTrackingHandler(collector) +logging.root.setLevel(logging.NOTSET) +logging.root.addHandler(logging_handler) # register with logging + +# We don't use enable/disable_instrumentation because we can't make these +# functions thread-safe and (hopefully) logging isn't too expensive. + +try: + import logbook + logbook_supported = True +except ImportError: + # logbook support is optional, so fail silently + logbook_supported = False + +if logbook_supported: + class LogbookThreadTrackingHandler(logbook.handlers.Handler): + def __init__(self, collector): + logbook.handlers.Handler.__init__(self, bubble=True) + self.collector = collector + + def emit(self, record): + record = { + 'message': record.message, + 'time': record.time, + 'level': record.level_name, + 'file': record.filename, + 'line': record.lineno, + 'channel': record.channel, + } + self.collector.add_record(record) + + logbook_handler = LogbookThreadTrackingHandler(collector) + logbook_handler.push_application() # register with logbook + + +class LoggingPanel(Panel): + name = 'Logging' + template = 'debug_toolbar/panels/logging.html' + has_content = True + + def __init__(self, *args, **kwargs): + super(LoggingPanel, self).__init__(*args, **kwargs) + self._records = {} + + def process_request(self, request): + collector.clear_records() + + def process_response(self, request, response): + records = self.get_and_delete() + self.record_stats({'records': records}) + + def get_and_delete(self): + records = collector.get_records() + self._records[threading.currentThread()] = records + collector.clear_records() + return records + + def nav_title(self): + return _("Logging") + + def nav_subtitle(self): + records = self._records[threading.currentThread()] + record_count = len(records) + return ungettext('%(count)s message', '%(count)s messages', + record_count) % {'count': record_count} + + def title(self): + return _('Log Messages') |
