Repository: django/django
Issue category: Performance Issue

Issue:
AdminEmailHandler wastes work when ADMINS isn’t set
Description
	 
AdminEmailHandler.emit() does a lot of work to assemble the message it passes to mail_admins. If settings.ADMINS is empty, mail_admins() returns instantly, wasting all the message-creation work. It’s quite common to not configure ADMINS, whether in lieu of more advanced tools like Sentry, or during tests.
In a quick benchmark on my M1 Mac Pro on Python 3.11, the overhead is ~2.5ms:
In [1]: import logging
In [2]: logger = logging.getLogger('django')
In [3]: %timeit logger.error("Yada")
...
2.78 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [4]: logger = logging.getLogger('example')
In [5]: %timeit logger.error("Yada")
...
8.37 µs ± 38.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
This can be avoided by adding an initial check to AdminEmailHandler.emit().


Hints:
Makes sense, thank you!, 1712655969

Patch:
diff --git a/django/utils/log.py b/django/utils/log.py
index fd0cc1bdc1ff..a25b97a7d5a4 100644
--- a/django/utils/log.py
+++ b/django/utils/log.py
@@ -92,6 +92,13 @@ def __init__(self, include_html=False, email_backend=None, reporter_class=None):
         )
 
     def emit(self, record):
+        # Early return when no email will be sent.
+        if (
+            not settings.ADMINS
+            # Method not overridden.
+            and self.send_mail.__func__ is AdminEmailHandler.send_mail
+        ):
+            return
         try:
             request = record.request
             subject = "%s (%s IP): %s" % (


Test patch:
diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py
index 20d2852fde00..610bdc112434 100644
--- a/tests/logging_tests/tests.py
+++ b/tests/logging_tests/tests.py
@@ -1,6 +1,7 @@
 import logging
 from contextlib import contextmanager
 from io import StringIO
+from unittest import mock
 
 from admin_scripts.tests import AdminScriptTestCase
 
@@ -470,6 +471,26 @@ def test_emit_no_form_tag(self):
         self.assertIn('<div id="traceback">', body_html)
         self.assertNotIn("<form", body_html)
 
+    @override_settings(ADMINS=[])
+    def test_emit_no_admins(self):
+        handler = AdminEmailHandler()
+        record = self.logger.makeRecord(
+            "name",
+            logging.ERROR,
+            "function",
+            "lno",
+            "message",
+            None,
+            None,
+        )
+        with mock.patch.object(
+            handler,
+            "format_subject",
+            side_effect=AssertionError("Should not be called"),
+        ):
+            handler.emit(record)
+        self.assertEqual(len(mail.outbox), 0)
+
 
 class SettingsConfigTest(AdminScriptTestCase):
     """
diff --git a/tests/view_tests/tests/test_defaults.py b/tests/view_tests/tests/test_defaults.py
index 415a9a8c6746..66bc1da16889 100644
--- a/tests/view_tests/tests/test_defaults.py
+++ b/tests/view_tests/tests/test_defaults.py
@@ -123,7 +123,7 @@ def test_bad_request(self):
     )
     def test_custom_bad_request_template(self):
         response = self.client.get("/raises400/")
-        self.assertIs(response.wsgi_request, response.context[-1].request)
+        self.assertIs(response.wsgi_request, response.context.request)
 
     @override_settings(
         TEMPLATES=[