| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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')
 |