Skip to content

Preserve logs that LoggingPanel would previously overwrite #1603

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

Merged
merged 4 commits into from
Apr 12, 2022
Merged
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
18 changes: 18 additions & 0 deletions debug_toolbar/panels/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ def emit(self, record):
self.collector.collect(record)


# Preserve Python's fallback log mechanism before adding ThreadTrackingHandler.

# If the root logger has no handlers attached then everything that reaches it goes
# to the "handler of last resort".
# So a Django app that doesn't explicitly configure the root logger actually logs
# through logging.lastResort.
# However, logging.lastResort is not used after ThreadTrackingHandler gets added to
# the root logger below. This means that users who have LoggingPanel enabled might
# find their logs are gone from their app as soon as they install DDT.
# Explicitly adding logging.lastResort to logging.root's handler sidesteps this
# potential confusion.
# Note that if root has already been configured, or logging.lastResort has been
# removed, then the configuration is unchanged, so users who configured their
# logging aren't exposed to the opposite confusion of seeing extra log lines from
# their app.
if not logging.root.hasHandlers() and logging.lastResort is not None:
logging.root.addHandler(logging.lastResort)

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

Expand Down
8 changes: 8 additions & 0 deletions tests/panels/test_logging.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from unittest.mock import patch

from debug_toolbar.panels.logging import (
MESSAGE_IF_STRING_REPRESENTATION_INVALID,
Expand Down Expand Up @@ -86,3 +87,10 @@ def view(request):
self.assertEqual(
MESSAGE_IF_STRING_REPRESENTATION_INVALID, records[0]["message"]
)

@patch("sys.stderr")
def test_fallback_logging(self, mock_stderr):
# make sure the log reaches stderr even though logging set up
# its own handler during its import
self.logger.warning("hello")
mock_stderr.write.assert_called_once_with("hello\n")
5 changes: 4 additions & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

INTERNAL_IPS = ["127.0.0.1"]

LOGGING_CONFIG = None # avoids spurious output in tests
LOGGING = { # avoids spurious output in tests
"version": 1,
"disable_existing_loggers": True,
}


# Application definition
Expand Down