mirror of
https://github.com/pallets-eco/flask-debugtoolbar.git
synced 2026-01-06 05:30:12 -06:00
Initialize the logging panel log handler on the first request so that werkzeug sets up its default logger first. Werkzeug now only adds its default log handler if no others are set up yet. So, when the logging panel set up its logger first, it suppressed the output from the development server.
107 lines
2.9 KiB
Python
107 lines
2.9 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():
|
|
# Initialize the logging handler once, but after werkzeug has set up its
|
|
# default logger. Otherwise, if this sets up the logging first, werkzeug
|
|
# will not create a default logger, so the development server's output will
|
|
# not get printed.
|
|
global handler
|
|
if handler is not None:
|
|
return
|
|
with _init_lock:
|
|
if handler is not None:
|
|
return
|
|
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)
|
|
|
|
|