Fixed #14861 -- Moved logging config outside of Settings.__init__
Thanks donspaulding for the report and simonpercivall for the initial patch.
This commit is contained in:
parent
e72e22e518
commit
fc69fff9ab
|
@ -43,13 +43,28 @@ class LazySettings(LazyObject):
|
|||
% (name, ENVIRONMENT_VARIABLE))
|
||||
|
||||
self._wrapped = Settings(settings_module)
|
||||
|
||||
self._configure_logging()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._wrapped is empty:
|
||||
self._setup(name)
|
||||
return getattr(self._wrapped, name)
|
||||
|
||||
def _configure_logging(self):
|
||||
"""
|
||||
Setup logging from LOGGING_CONFIG and LOGGING settings.
|
||||
"""
|
||||
if self.LOGGING_CONFIG:
|
||||
# First find the logging configuration function ...
|
||||
logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
|
||||
logging_config_module = importlib.import_module(logging_config_path)
|
||||
logging_config_func = getattr(logging_config_module, logging_config_func_name)
|
||||
|
||||
# Backwards-compatibility shim for #16288 fix
|
||||
compat_patch_logging_config(self.LOGGING)
|
||||
|
||||
# ... then invoke it with the logging settings
|
||||
logging_config_func(self.LOGGING)
|
||||
|
||||
def configure(self, default_settings=global_settings, **options):
|
||||
"""
|
||||
|
@ -133,19 +148,6 @@ class Settings(BaseSettings):
|
|||
os.environ['TZ'] = self.TIME_ZONE
|
||||
time.tzset()
|
||||
|
||||
# Settings are configured, so we can set up the logger if required
|
||||
if self.LOGGING_CONFIG:
|
||||
# First find the logging configuration function ...
|
||||
logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
|
||||
logging_config_module = importlib.import_module(logging_config_path)
|
||||
logging_config_func = getattr(logging_config_module, logging_config_func_name)
|
||||
|
||||
# Backwards-compatibility shim for #16288 fix
|
||||
compat_patch_logging_config(self.LOGGING)
|
||||
|
||||
# ... then invoke it with the logging settings
|
||||
logging_config_func(self.LOGGING)
|
||||
|
||||
|
||||
class UserSettingsHolder(BaseSettings):
|
||||
"""
|
||||
|
|
|
@ -345,36 +345,6 @@ This logging configuration does the following things:
|
|||
printed to the console; ``ERROR`` and ``CRITICAL``
|
||||
messages will also be output via email.
|
||||
|
||||
.. admonition:: Custom handlers and circular imports
|
||||
|
||||
If your ``settings.py`` specifies a custom handler class and the file
|
||||
defining that class also imports ``settings.py`` a circular import will
|
||||
occur.
|
||||
|
||||
For example, if ``settings.py`` contains the following config for
|
||||
:setting:`LOGGING`::
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'handlers': {
|
||||
'custom_handler': {
|
||||
'level': 'INFO',
|
||||
'class': 'myproject.logconfig.MyHandler',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
and ``myproject/logconfig.py`` has the following line before the
|
||||
``MyHandler`` definition::
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
then the ``dictconfig`` module will raise an exception like the following::
|
||||
|
||||
ValueError: Unable to configure handler 'custom_handler':
|
||||
Unable to configure handler 'custom_handler':
|
||||
'module' object has no attribute 'logconfig'
|
||||
|
||||
.. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects
|
||||
|
||||
Custom logging configuration
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
class MyHandler(logging.Handler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.config = settings.LOGGING
|
|
@ -10,6 +10,8 @@ from django.test import TestCase, RequestFactory
|
|||
from django.test.utils import override_settings
|
||||
from django.utils.log import CallbackFilter, RequireDebugFalse
|
||||
|
||||
from ..admin_scripts.tests import AdminScriptTestCase
|
||||
|
||||
|
||||
# logging config prior to using filter with mail_admins
|
||||
OLD_LOGGING = {
|
||||
|
@ -253,3 +255,30 @@ class AdminEmailHandlerTest(TestCase):
|
|||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, expected_subject)
|
||||
|
||||
|
||||
class SettingsConfigTest(AdminScriptTestCase):
|
||||
"""
|
||||
Test that accessing settings in a custom logging handler does not trigger
|
||||
a circular import error.
|
||||
"""
|
||||
def setUp(self):
|
||||
log_config = """{
|
||||
'version': 1,
|
||||
'handlers': {
|
||||
'custom_handler': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging_tests.logconfig.MyHandler',
|
||||
}
|
||||
}
|
||||
}"""
|
||||
self.write_settings('settings.py', sdict={'LOGGING': log_config})
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_settings('settings.py')
|
||||
|
||||
def test_circular_dependency(self):
|
||||
# validate is just an example command to trigger settings configuration
|
||||
out, err = self.run_manage(['validate'])
|
||||
self.assertNoOutput(err)
|
||||
self.assertOutput(out, "0 errors found")
|
||||
|
|
Loading…
Reference in New Issue