Ticket #14861: 14861-3.diff

File 14861-3.diff, 5.3 KB (added by claudep, 3 years ago)

Move logging config outside of Settings.init

  • django/conf/__init__.py

    diff --git a/django/conf/__init__.py b/django/conf/__init__.py
    index f4d17ca..2fb2963 100644
    a b class LazySettings(LazyObject): 
    4343
    4444        self._wrapped = Settings(settings_module)
    4545
     46        # Settings are configured, so we can set up the logger if required
     47        if self._wrapped.LOGGING_CONFIG:
     48            # First find the logging configuration function ...
     49            logging_config_path, logging_config_func_name = self._wrapped.LOGGING_CONFIG.rsplit('.', 1)
     50            logging_config_module = importlib.import_module(logging_config_path)
     51            logging_config_func = getattr(logging_config_module, logging_config_func_name)
     52
     53            # Backwards-compatibility shim for #16288 fix
     54            compat_patch_logging_config(self._wrapped.LOGGING)
     55
     56            # ... then invoke it with the logging settings
     57            logging_config_func(self._wrapped.LOGGING)
     58
    4659    def configure(self, default_settings=global_settings, **options):
    4760        """
    4861        Called to manually configure the settings. The 'default_settings'
    class Settings(BaseSettings): 
    125138            os.environ['TZ'] = self.TIME_ZONE
    126139            time.tzset()
    127140
    128         # Settings are configured, so we can set up the logger if required
    129         if self.LOGGING_CONFIG:
    130             # First find the logging configuration function ...
    131             logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
    132             logging_config_module = importlib.import_module(logging_config_path)
    133             logging_config_func = getattr(logging_config_module, logging_config_func_name)
    134 
    135             # Backwards-compatibility shim for #16288 fix
    136             compat_patch_logging_config(self.LOGGING)
    137 
    138             # ... then invoke it with the logging settings
    139             logging_config_func(self.LOGGING)
    140 
    141141
    142142class UserSettingsHolder(BaseSettings):
    143143    """
  • docs/topics/logging.txt

    diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
    index b54a947..d7e97ea 100644
    a b This logging configuration does the following things: 
    347347    printed to the console; ``ERROR`` and ``CRITICAL``
    348348    messages will also be output via email.
    349349
    350 .. admonition:: Custom handlers and circular imports
    351 
    352     If your ``settings.py`` specifies a custom handler class and the file
    353     defining that class also imports ``settings.py`` a circular import will
    354     occur.
    355 
    356     For example, if ``settings.py`` contains the following config for
    357     :setting:`LOGGING`::
    358 
    359         LOGGING = {
    360           'version': 1,
    361           'handlers': {
    362             'custom_handler': {
    363               'level': 'INFO',
    364               'class': 'myproject.logconfig.MyHandler',
    365             }
    366           }
    367         }
    368 
    369     and ``myproject/logconfig.py`` has the following line before the
    370     ``MyHandler`` definition::
    371 
    372         from django.conf import settings
    373 
    374     then the ``dictconfig`` module will raise an exception like the following::
    375 
    376         ValueError: Unable to configure handler 'custom_handler':
    377         Unable to configure handler 'custom_handler':
    378         'module' object has no attribute 'logconfig'
    379 
    380 .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects
    381 
    382350Custom logging configuration
    383351----------------------------
    384352
  • new file tests/regressiontests/logging_tests/logconfig.py

    diff --git a/tests/regressiontests/logging_tests/logconfig.py b/tests/regressiontests/logging_tests/logconfig.py
    new file mode 100644
    index 0000000..8524aa2
    - +  
     1import logging
     2
     3from django.conf import settings
     4
     5class MyHandler(logging.Handler):
     6    def __init__(self, *args, **kwargs):
     7        self.config = settings.LOGGING
  • tests/regressiontests/logging_tests/tests.py

    diff --git a/tests/regressiontests/logging_tests/tests.py b/tests/regressiontests/logging_tests/tests.py
    index 4ae3c1b..fe65fca 100644
    a b from django.test import TestCase, RequestFactory 
    99from django.test.utils import override_settings
    1010from django.utils.log import CallbackFilter, RequireDebugFalse, getLogger
    1111
     12from ..admin_scripts.tests import AdminScriptTestCase
     13
    1214
    1315# logging config prior to using filter with mail_admins
    1416OLD_LOGGING = {
    class AdminEmailHandlerTest(TestCase): 
    252254
    253255        self.assertEqual(len(mail.outbox), 1)
    254256        self.assertEqual(mail.outbox[0].subject, expected_subject)
     257
     258
     259class SettingsConfigTest(AdminScriptTestCase):
     260    """
     261    Test that accessing settings in a custom logging handler does not trigger
     262    a circular import error.
     263    """
     264    def setUp(self):
     265        log_config = """{
     266    'version': 1,
     267    'handlers': {
     268        'custom_handler': {
     269            'level': 'INFO',
     270            'class': 'logging_tests.logconfig.MyHandler',
     271        }
     272    }
     273}"""
     274        self.write_settings('settings.py', sdict={'LOGGING': log_config})
     275
     276    def tearDown(self):
     277        self.remove_settings('settings.py')
     278
     279    def test_circular_dependency(self):
     280        # validate is just an example command to trigger settings configuration
     281        out, err = self.run_manage(['validate'])
     282        self.assertNoOutput(err)
     283        self.assertOutput(out, "0 errors found")
Back to Top