Skip to content

Disable Logging panel when DEBUG is set to False to prevent memory leaks #563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions debug_toolbar/panels/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import threading
except ImportError:
threading = None
from django.conf import settings
from django.utils.translation import ungettext, ugettext_lazy as _
from debug_toolbar.panels import Panel
from debug_toolbar.utils import ThreadCollector
Expand All @@ -15,13 +16,33 @@

class LogCollector(ThreadCollector):

def __init__(self):
ThreadCollector.__init__(self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason for using ThreadCollecter instead of super()? If so, I think it should be documented.

self.enabled = False

def collect(self, item, thread=None):
# Avoid logging SQL queries since they are already in the SQL panel
# TODO: Make this check whether SQL panel is enabled
if item.get('channel', '') == 'django.db.backends':
return
super(LogCollector, self).collect(item, thread)

def enable_logging(self):
"""
Enable logging if it has not already been enabled and DEBUG is True.
"""
if self.enabled:
return

# Check to make sure DEBUG is enabled to prevent silent memory leaks.
if settings.DEBUG:
# We don't use enable/disable_instrumentation because logging is global.
# We can't add thread-local logging handlers. Hopefully logging is cheap.
logging_handler = ThreadTrackingHandler(collector)
logging.root.setLevel(logging.NOTSET)
logging.root.addHandler(logging_handler)
self.enabled = True


class ThreadTrackingHandler(logging.Handler):
def __init__(self, collector):
Expand All @@ -45,13 +66,8 @@ def emit(self, record):
self.collector.collect(record)


# We don't use enable/disable_instrumentation because logging is global.
# We can't add thread-local logging handlers. Hopefully logging is cheap.

collector = LogCollector()
logging_handler = ThreadTrackingHandler(collector)
logging.root.setLevel(logging.NOTSET)
logging.root.addHandler(logging_handler)
collector.enable_logging()


class LoggingPanel(Panel):
Expand Down
1 change: 1 addition & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ New features

* The SQL panel colors queries depending on the stack level.
* The Profiler panel allows configuring the maximum depth.
* The Logging panel will not attempt to collect data when DEBUG is False.

Bugfixes
~~~~~~~~
Expand Down
5 changes: 5 additions & 0 deletions tests/panels/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@

from debug_toolbar.panels.logging import (
collector, MESSAGE_IF_STRING_REPRESENTATION_INVALID)
from django.test.utils import override_settings

from ..base import BaseTestCase


@override_settings(DEBUG=True)
class LoggingPanelTestCase(BaseTestCase):

def setUp(self):
super(LoggingPanelTestCase, self).setUp()
self.panel = self.toolbar.get_panel_by_id('LoggingPanel')
self.logger = logging.getLogger(__name__)
# DEBUG may be set to False initially, preventing the default tracking
# from executing. Force an enable here to ensure that logging is activated.
collector.enable_logging()
collector.clear_collection()

def test_happy_case(self):
Expand Down