diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index 6272f4e..d636ff0 100644
|
a
|
b
|
class LazySettings(LazyObject):
|
| 43 | 43 | % (name, ENVIRONMENT_VARIABLE)) |
| 44 | 44 | |
| 45 | 45 | self._wrapped = Settings(settings_module) |
| 46 | | |
| | 46 | self._configure_logging() |
| 47 | 47 | |
| 48 | 48 | def __getattr__(self, name): |
| 49 | 49 | if self._wrapped is empty: |
| 50 | 50 | self._setup(name) |
| 51 | 51 | return getattr(self._wrapped, name) |
| 52 | 52 | |
| | 53 | def _configure_logging(self): |
| | 54 | """ |
| | 55 | Setup logging from LOGGING_CONFIG and LOGGING settings. |
| | 56 | """ |
| | 57 | if self.LOGGING_CONFIG: |
| | 58 | # First find the logging configuration function ... |
| | 59 | logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1) |
| | 60 | logging_config_module = importlib.import_module(logging_config_path) |
| | 61 | logging_config_func = getattr(logging_config_module, logging_config_func_name) |
| | 62 | |
| | 63 | # Backwards-compatibility shim for #16288 fix |
| | 64 | compat_patch_logging_config(self.LOGGING) |
| | 65 | |
| | 66 | # ... then invoke it with the logging settings |
| | 67 | logging_config_func(self.LOGGING) |
| 53 | 68 | |
| 54 | 69 | def configure(self, default_settings=global_settings, **options): |
| 55 | 70 | """ |
| … |
… |
class Settings(BaseSettings):
|
| 133 | 148 | os.environ['TZ'] = self.TIME_ZONE |
| 134 | 149 | time.tzset() |
| 135 | 150 | |
| 136 | | # Settings are configured, so we can set up the logger if required |
| 137 | | if self.LOGGING_CONFIG: |
| 138 | | # First find the logging configuration function ... |
| 139 | | logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1) |
| 140 | | logging_config_module = importlib.import_module(logging_config_path) |
| 141 | | logging_config_func = getattr(logging_config_module, logging_config_func_name) |
| 142 | | |
| 143 | | # Backwards-compatibility shim for #16288 fix |
| 144 | | compat_patch_logging_config(self.LOGGING) |
| 145 | | |
| 146 | | # ... then invoke it with the logging settings |
| 147 | | logging_config_func(self.LOGGING) |
| 148 | | |
| 149 | 151 | |
| 150 | 152 | class UserSettingsHolder(BaseSettings): |
| 151 | 153 | """ |
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index 94236ba..5fc229e 100644
|
a
|
b
|
This logging configuration does the following things:
|
| 347 | 347 | |
| 348 | 348 | .. admonition:: Custom handlers and circular imports |
| 349 | 349 | |
| 350 | | If your ``settings.py`` specifies a custom handler class and the file |
| 351 | | defining that class also imports ``settings.py`` a circular import will |
| 352 | | occur. |
| 353 | | |
| 354 | | For example, if ``settings.py`` contains the following config for |
| 355 | | :setting:`LOGGING`:: |
| 356 | | |
| 357 | | LOGGING = { |
| 358 | | 'version': 1, |
| 359 | | 'handlers': { |
| 360 | | 'custom_handler': { |
| 361 | | 'level': 'INFO', |
| 362 | | 'class': 'myproject.logconfig.MyHandler', |
| 363 | | } |
| 364 | | } |
| 365 | | } |
| 366 | | |
| 367 | | and ``myproject/logconfig.py`` has the following line before the |
| 368 | | ``MyHandler`` definition:: |
| 369 | | |
| 370 | | from django.conf import settings |
| 371 | | |
| 372 | | then the ``dictconfig`` module will raise an exception like the following:: |
| 373 | | |
| 374 | | ValueError: Unable to configure handler 'custom_handler': |
| 375 | | Unable to configure handler 'custom_handler': |
| 376 | | 'module' object has no attribute 'logconfig' |
| | 350 | Logging configuration happens soon in the Django setup machinery. If your |
| | 351 | ``settings.py`` specifies a custom handler class, you should be careful |
| | 352 | about the modules you import in the file defining this class, unless a |
| | 353 | circular import may occur. |
| 377 | 354 | |
| 378 | 355 | .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects |
| 379 | 356 | |
diff --git a/tests/regressiontests/logging_tests/logconfig.py b/tests/regressiontests/logging_tests/logconfig.py
new file mode 100644
index 0000000..8524aa2
|
-
|
+
|
|
| | 1 | import logging |
| | 2 | |
| | 3 | from django.conf import settings |
| | 4 | |
| | 5 | class MyHandler(logging.Handler): |
| | 6 | def __init__(self, *args, **kwargs): |
| | 7 | self.config = settings.LOGGING |
diff --git a/tests/regressiontests/logging_tests/tests.py b/tests/regressiontests/logging_tests/tests.py
index f444e0f..a54b425 100644
|
a
|
b
|
from django.test import TestCase, RequestFactory
|
| 10 | 10 | from django.test.utils import override_settings |
| 11 | 11 | from django.utils.log import CallbackFilter, RequireDebugFalse |
| 12 | 12 | |
| | 13 | from ..admin_scripts.tests import AdminScriptTestCase |
| | 14 | |
| 13 | 15 | |
| 14 | 16 | # logging config prior to using filter with mail_admins |
| 15 | 17 | OLD_LOGGING = { |
| … |
… |
class AdminEmailHandlerTest(TestCase):
|
| 253 | 255 | |
| 254 | 256 | self.assertEqual(len(mail.outbox), 1) |
| 255 | 257 | self.assertEqual(mail.outbox[0].subject, expected_subject) |
| | 258 | |
| | 259 | |
| | 260 | class SettingsConfigTest(AdminScriptTestCase): |
| | 261 | """ |
| | 262 | Test that accessing settings in a custom logging handler does not trigger |
| | 263 | a circular import error. |
| | 264 | """ |
| | 265 | def setUp(self): |
| | 266 | log_config = """{ |
| | 267 | 'version': 1, |
| | 268 | 'handlers': { |
| | 269 | 'custom_handler': { |
| | 270 | 'level': 'INFO', |
| | 271 | 'class': 'logging_tests.logconfig.MyHandler', |
| | 272 | } |
| | 273 | } |
| | 274 | }""" |
| | 275 | self.write_settings('settings.py', sdict={'LOGGING': log_config}) |
| | 276 | |
| | 277 | def tearDown(self): |
| | 278 | self.remove_settings('settings.py') |
| | 279 | |
| | 280 | def test_circular_dependency(self): |
| | 281 | # validate is just an example command to trigger settings configuration |
| | 282 | out, err = self.run_manage(['validate']) |
| | 283 | self.assertNoOutput(err) |
| | 284 | self.assertOutput(out, "0 errors found") |