mirror of
https://github.com/pallets-eco/flask-debugtoolbar.git
synced 2026-05-21 00:28:37 -05:00
3bea63dc8a
Werkzeug will disable its default logging setup if another log handler is already configured. At some point the initialization order changed and the logging panel's handler is getting added first now, so werkzeug's request log will not be printed to the console by default. By explicitly calling werkzeug's logger we now make sure it's initialized before the logging panel's handler. Fixes #33
116 lines
3.2 KiB
Python
116 lines
3.2 KiB
Python
from __future__ import with_statement
|
|
|
|
import datetime
|
|
import logging
|
|
try:
|
|
import threading
|
|
except ImportError:
|
|
threading = None
|
|
|
|
from flask_debugtoolbar.panels import DebugPanel
|
|
from flask_debugtoolbar.utils import format_fname
|
|
|
|
_ = lambda x: x
|
|
|
|
class ThreadTrackingHandler(logging.Handler):
|
|
def __init__(self):
|
|
if threading is None:
|
|
raise NotImplementedError("threading module is not available, \
|
|
the logging panel cannot be used without it")
|
|
logging.Handler.__init__(self)
|
|
self.records = {} # a dictionary that maps threads to log records
|
|
|
|
def emit(self, record):
|
|
self.get_records().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]
|
|
|
|
|
|
handler = None
|
|
_init_lock = threading.Lock()
|
|
|
|
|
|
def _init_once():
|
|
global handler
|
|
if handler is not None:
|
|
return
|
|
with _init_lock:
|
|
if handler is not None:
|
|
return
|
|
|
|
# Call werkzeug's internal logging to make sure it gets configured
|
|
# before we add our handler. Otherwise werkzeug will see our handler
|
|
# and not configure console logging for the request log.
|
|
# Werkzeug's default log level is INFO so this message probably won't be
|
|
# seen.
|
|
try:
|
|
from werkzeug._internal import _log
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
_log('debug', 'Initializing Flask-DebugToolbar log handler')
|
|
|
|
handler = ThreadTrackingHandler()
|
|
logging.root.addHandler(handler)
|
|
|
|
|
|
class LoggingPanel(DebugPanel):
|
|
name = 'Logging'
|
|
has_content = True
|
|
|
|
def process_request(self, request):
|
|
_init_once()
|
|
handler.clear_records()
|
|
|
|
def get_and_delete(self):
|
|
records = handler.get_records()
|
|
handler.clear_records()
|
|
return records
|
|
|
|
def nav_title(self):
|
|
return _("Logging")
|
|
|
|
def nav_subtitle(self):
|
|
# FIXME l10n: use ngettext
|
|
return "%s message%s" % (len(handler.get_records()), (len(handler.get_records()) == 1) and '' or 's')
|
|
|
|
def title(self):
|
|
return _('Log Messages')
|
|
|
|
def url(self):
|
|
return ''
|
|
|
|
def content(self):
|
|
records = []
|
|
for record in self.get_and_delete():
|
|
records.append({
|
|
'message': record.getMessage(),
|
|
'time': datetime.datetime.fromtimestamp(record.created),
|
|
'level': record.levelname,
|
|
'file': format_fname(record.pathname),
|
|
'file_long': record.pathname,
|
|
'line': record.lineno,
|
|
})
|
|
|
|
context = self.context.copy()
|
|
context.update({'records': records})
|
|
|
|
return self.render('panels/logger.html', context)
|
|
|
|
|