diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index 4337bd4..3b829ce 100644
a
|
b
|
def compat_patch_logging_config(logging_config):
|
199 | 199 | while filter_name in filters: |
200 | 200 | filter_name = filter_name + "_" |
201 | 201 | |
202 | | def _callback(record): |
203 | | from django.conf import settings |
204 | | return not settings.DEBUG |
205 | | |
206 | 202 | filters[filter_name] = { |
207 | | "()": "django.utils.log.CallbackFilter", |
208 | | "callback": _callback |
209 | | } |
| 203 | "()": "django.utils.log.RequireDebugFalse", |
| 204 | } |
210 | 205 | |
211 | 206 | logging_config["handlers"]["mail_admins"]["filters"] = [filter_name] |
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 5ee12d7..4eb31f5 100644
a
|
b
|
LOGGING_CONFIG = 'django.utils.log.dictConfig'
|
522 | 522 | # The default logging configuration. This sends an email to |
523 | 523 | # the site admins on every HTTP 500 error. All other log |
524 | 524 | # records are sent to the bit bucket. |
| 525 | |
525 | 526 | LOGGING = { |
526 | 527 | 'version': 1, |
527 | 528 | 'disable_existing_loggers': False, |
528 | 529 | 'filters': { |
529 | 530 | 'require_debug_false': { |
530 | | '()': 'django.utils.log.CallbackFilter', |
531 | | 'callback': lambda r: not DEBUG |
| 531 | '()': 'django.utils.log.RequireDebugFalse', |
532 | 532 | } |
533 | 533 | }, |
534 | 534 | 'handlers': { |
diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py
index 3a2243f..b92c116 100644
a
|
b
|
LOGGING = {
|
128 | 128 | 'disable_existing_loggers': False, |
129 | 129 | 'filters': { |
130 | 130 | 'require_debug_false': { |
131 | | '()': 'django.utils.log.CallbackFilter', |
132 | | 'callback': lambda r: not DEBUG |
| 131 | '()': 'django.utils.log.RequireDebugFalse' |
133 | 132 | } |
134 | 133 | }, |
135 | 134 | 'handlers': { |
diff --git a/django/utils/log.py b/django/utils/log.py
index 1c3fe70..1da7df0 100644
a
|
b
|
logger = getLogger('django')
|
30 | 30 | if not logger.handlers: |
31 | 31 | logger.addHandler(NullHandler()) |
32 | 32 | |
| 33 | |
33 | 34 | class AdminEmailHandler(logging.Handler): |
34 | 35 | def __init__(self, include_html=False): |
35 | 36 | logging.Handler.__init__(self) |
… |
… |
class CallbackFilter(logging.Filter):
|
81 | 82 | def __init__(self, callback): |
82 | 83 | self.callback = callback |
83 | 84 | |
84 | | |
85 | 85 | def filter(self, record): |
86 | 86 | if self.callback(record): |
87 | 87 | return 1 |
88 | 88 | return 0 |
| 89 | |
| 90 | |
| 91 | class RequireDebugFalse(logging.Filter): |
| 92 | def filter(self, record): |
| 93 | return not settings.DEBUG |
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 2723b42..a775dad 100644
a
|
b
|
to :class:`django.utils.log.AdminEmailHandler` to prevent admin error emails in
|
485 | 485 | |
486 | 486 | 'filters': { |
487 | 487 | 'require_debug_false': { |
488 | | '()': 'django.utils.log.CallbackFilter', |
489 | | 'callback': lambda r: not DEBUG |
| 488 | '()': 'django.utils.log.RequireDebugFalse' |
490 | 489 | } |
491 | 490 | }, |
492 | 491 | 'handlers': { |
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index 9137038..472dcff 100644
a
|
b
|
Python logging module.
|
506 | 506 | Filters |
507 | 507 | ------- |
508 | 508 | |
509 | | Django provides one log filter in addition to those provided by the |
| 509 | Django provides two log filter in addition to those provided by the |
510 | 510 | Python logging module. |
511 | 511 | |
512 | 512 | .. class:: CallbackFilter(callback) |
… |
… |
Python logging module.
|
518 | 518 | through the filter. Handling of that record will not proceed if the callback |
519 | 519 | returns False. |
520 | 520 | |
| 521 | .. class:: RequireDebugFalse() |
| 522 | |
| 523 | .. versionadded:: 1.4 |
| 524 | |
| 525 | This filter will only proceed records when settings.DEBUG is False. |
| 526 | |
521 | 527 | This filter is used as follows in the default :setting:`LOGGING` |
522 | 528 | configuration to ensure that the :class:`AdminEmailHandler` only sends error |
523 | 529 | emails to admins when :setting:`DEBUG` is `False`:: |
524 | 530 | |
525 | 531 | 'filters': { |
526 | 532 | 'require_debug_false': { |
527 | | '()': 'django.utils.log.CallbackFilter', |
528 | | 'callback': lambda r: not DEBUG |
| 533 | '()': 'django.utils.log.RequireDebugFalse', |
529 | 534 | } |
530 | 535 | }, |
531 | 536 | 'handlers': { |
diff --git a/tests/regressiontests/logging_tests/tests.py b/tests/regressiontests/logging_tests/tests.py
index fe8b169..b34ca78 100644
a
|
b
|
from __future__ import with_statement
|
2 | 2 | |
3 | 3 | import copy |
4 | 4 | |
5 | | from django.conf import compat_patch_logging_config |
| 5 | from django.conf import compat_patch_logging_config, settings, global_settings |
6 | 6 | from django.test import TestCase |
7 | | from django.utils.log import CallbackFilter |
| 7 | from django.utils.log import CallbackFilter, RequireDebugFalse |
8 | 8 | |
9 | 9 | |
10 | 10 | # logging config prior to using filter with mail_admins |
… |
… |
OLD_LOGGING = {
|
27 | 27 | } |
28 | 28 | |
29 | 29 | |
30 | | |
31 | 30 | class PatchLoggingConfigTest(TestCase): |
32 | 31 | """ |
33 | 32 | Tests for backward-compat shim for #16288. These tests should be removed in |
… |
… |
class PatchLoggingConfigTest(TestCase):
|
47 | 46 | config["handlers"]["mail_admins"]["filters"], |
48 | 47 | ['require_debug_false']) |
49 | 48 | |
50 | | |
51 | 49 | def test_filter_configuration(self): |
52 | 50 | """ |
53 | | Test that the debug-false filter is a CallbackFilter with a callback |
54 | | that works as expected (returns ``not DEBUG``). |
| 51 | Test that the require_debug_false filter (RequireDebugFalse) is added |
| 52 | to the settings when not explicitly specified in the settings. |
55 | 53 | |
56 | 54 | """ |
57 | 55 | config = copy.deepcopy(OLD_LOGGING) |
58 | 56 | compat_patch_logging_config(config) |
59 | 57 | |
60 | 58 | flt = config["filters"]["require_debug_false"] |
| 59 | self.assertEqual(flt["()"], "django.utils.log.RequireDebugFalse") |
61 | 60 | |
62 | | self.assertEqual(flt["()"], "django.utils.log.CallbackFilter") |
63 | | |
64 | | callback = flt["callback"] |
| 61 | def test_require_debug_false_filter(self): |
| 62 | """Test the RequireDebugFalse filter""" |
| 63 | filter_ = RequireDebugFalse() |
65 | 64 | |
66 | 65 | with self.settings(DEBUG=True): |
67 | | self.assertEqual(callback("record is not used"), False) |
| 66 | self.assertEqual(filter_.filter("record is not used"), False) |
68 | 67 | |
69 | 68 | with self.settings(DEBUG=False): |
70 | | self.assertEqual(callback("record is not used"), True) |
71 | | |
| 69 | self.assertEqual(filter_.filter("record is not used"), True) |
72 | 70 | |
73 | 71 | def test_no_patch_if_filters_key_exists(self): |
74 | 72 | """ |
… |
… |
class PatchLoggingConfigTest(TestCase):
|
97 | 95 | self.assertEqual(config, new_config) |
98 | 96 | |
99 | 97 | |
| 98 | class TestRequireDebugFalseNoLoggingSetting(TestCase): |
| 99 | """ |
| 100 | Test that RequireDebugFalse works as excpected when LOGGING is not |
| 101 | specified in the project settings. See #16568. |
| 102 | """ |
| 103 | def setUp(self): |
| 104 | self.old_logging = settings.LOGGING |
| 105 | settings.LOGGING = global_settings.LOGGING |
| 106 | |
| 107 | def tearDown(self): |
| 108 | settings.LOGGING = self.old_logging |
| 109 | |
| 110 | def test_require_debug_no_logging_setting(self): |
| 111 | filter_ = RequireDebugFalse() |
| 112 | |
| 113 | with self.settings(DEBUG=True): |
| 114 | self.assertEqual(filter_.filter("record is not used"), False) |
| 115 | |
| 116 | with self.settings(DEBUG=False): |
| 117 | self.assertEqual(filter_.filter("record is not used"), True) |
| 118 | |
| 119 | |
100 | 120 | class CallbackFilterTest(TestCase): |
101 | 121 | def test_sense(self): |
102 | 122 | f_false = CallbackFilter(lambda r: False) |
… |
… |
class CallbackFilterTest(TestCase):
|
107 | 127 | |
108 | 128 | def test_passes_on_record(self): |
109 | 129 | collector = [] |
| 130 | |
110 | 131 | def _callback(record): |
111 | 132 | collector.append(record) |
112 | 133 | return True |