Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/utils/log.py: 46%
98 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import logging
2import logging.config # needed when logging_config doesn't start with logging.config
3from copy import copy
5from django.conf import settings
6from django.core import mail
7from django.core.mail import get_connection
8from django.core.management.color import color_style
9from django.utils.module_loading import import_string
11request_logger = logging.getLogger("django.request")
13# Default logging for Django. This sends an email to the site admins on every
14# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
15# the console (DEBUG=True) or discarded (DEBUG=False) by means of the
16# require_debug_true filter. This configuration is quoted in
17# docs/ref/logging.txt; please amend it there if edited here.
18DEFAULT_LOGGING = {
19 "version": 1,
20 "disable_existing_loggers": False,
21 "filters": {
22 "require_debug_false": {
23 "()": "django.utils.log.RequireDebugFalse",
24 },
25 "require_debug_true": {
26 "()": "django.utils.log.RequireDebugTrue",
27 },
28 },
29 "formatters": {
30 "django.server": {
31 "()": "django.utils.log.ServerFormatter",
32 "format": "[{server_time}] {message}",
33 "style": "{",
34 }
35 },
36 "handlers": {
37 "console": {
38 "level": "INFO",
39 "filters": ["require_debug_true"],
40 "class": "logging.StreamHandler",
41 },
42 "django.server": {
43 "level": "INFO",
44 "class": "logging.StreamHandler",
45 "formatter": "django.server",
46 },
47 "mail_admins": {
48 "level": "ERROR",
49 "filters": ["require_debug_false"],
50 "class": "django.utils.log.AdminEmailHandler",
51 },
52 },
53 "loggers": {
54 "django": {
55 "handlers": ["console", "mail_admins"],
56 "level": "INFO",
57 },
58 "django.server": {
59 "handlers": ["django.server"],
60 "level": "INFO",
61 "propagate": False,
62 },
63 },
64}
67def configure_logging(logging_config, logging_settings):
68 if logging_config: 68 ↛ exitline 68 didn't return from function 'configure_logging', because the condition on line 68 was never false
69 # First find the logging configuration function ...
70 logging_config_func = import_string(logging_config)
72 logging.config.dictConfig(DEFAULT_LOGGING)
74 # ... then invoke it with the logging settings
75 if logging_settings: 75 ↛ 76line 75 didn't jump to line 76, because the condition on line 75 was never true
76 logging_config_func(logging_settings)
79class AdminEmailHandler(logging.Handler):
80 """An exception log handler that emails log entries to site admins.
82 If the request is passed as the first argument to the log record,
83 request data will be provided in the email report.
84 """
86 def __init__(self, include_html=False, email_backend=None, reporter_class=None):
87 super().__init__()
88 self.include_html = include_html
89 self.email_backend = email_backend
90 self.reporter_class = import_string(
91 reporter_class or settings.DEFAULT_EXCEPTION_REPORTER
92 )
94 def emit(self, record):
95 try:
96 request = record.request
97 subject = "%s (%s IP): %s" % (
98 record.levelname,
99 (
100 "internal"
101 if request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS
102 else "EXTERNAL"
103 ),
104 record.getMessage(),
105 )
106 except Exception:
107 subject = "%s: %s" % (record.levelname, record.getMessage())
108 request = None
109 subject = self.format_subject(subject)
111 # Since we add a nicely formatted traceback on our own, create a copy
112 # of the log record without the exception data.
113 no_exc_record = copy(record)
114 no_exc_record.exc_info = None
115 no_exc_record.exc_text = None
117 if record.exc_info:
118 exc_info = record.exc_info
119 else:
120 exc_info = (None, record.getMessage(), None)
122 reporter = self.reporter_class(request, is_email=True, *exc_info)
123 message = "%s\n\n%s" % (
124 self.format(no_exc_record),
125 reporter.get_traceback_text(),
126 )
127 html_message = reporter.get_traceback_html() if self.include_html else None
128 self.send_mail(subject, message, fail_silently=True, html_message=html_message)
130 def send_mail(self, subject, message, *args, **kwargs):
131 mail.mail_admins(
132 subject, message, *args, connection=self.connection(), **kwargs
133 )
135 def connection(self):
136 return get_connection(backend=self.email_backend, fail_silently=True)
138 def format_subject(self, subject):
139 """
140 Escape CR and LF characters.
141 """
142 return subject.replace("\n", "\\n").replace("\r", "\\r")
145class CallbackFilter(logging.Filter):
146 """
147 A logging filter that checks the return value of a given callable (which
148 takes the record-to-be-logged as its only parameter) to decide whether to
149 log a record.
150 """
152 def __init__(self, callback):
153 self.callback = callback
155 def filter(self, record):
156 if self.callback(record):
157 return 1
158 return 0
161class RequireDebugFalse(logging.Filter):
162 def filter(self, record):
163 return not settings.DEBUG
166class RequireDebugTrue(logging.Filter):
167 def filter(self, record):
168 return settings.DEBUG
171class ServerFormatter(logging.Formatter):
172 default_time_format = "%d/%b/%Y %H:%M:%S"
174 def __init__(self, *args, **kwargs):
175 self.style = color_style()
176 super().__init__(*args, **kwargs)
178 def format(self, record):
179 msg = record.msg
180 status_code = getattr(record, "status_code", None)
182 if status_code:
183 if 200 <= status_code < 300:
184 # Put 2XX first, since it should be the common case
185 msg = self.style.HTTP_SUCCESS(msg)
186 elif 100 <= status_code < 200:
187 msg = self.style.HTTP_INFO(msg)
188 elif status_code == 304:
189 msg = self.style.HTTP_NOT_MODIFIED(msg)
190 elif 300 <= status_code < 400:
191 msg = self.style.HTTP_REDIRECT(msg)
192 elif status_code == 404:
193 msg = self.style.HTTP_NOT_FOUND(msg)
194 elif 400 <= status_code < 500:
195 msg = self.style.HTTP_BAD_REQUEST(msg)
196 else:
197 # Any 5XX, or any other status code
198 msg = self.style.HTTP_SERVER_ERROR(msg)
200 if self.uses_server_time() and not hasattr(record, "server_time"):
201 record.server_time = self.formatTime(record, self.datefmt)
203 record.msg = msg
204 return super().format(record)
206 def uses_server_time(self):
207 return self._fmt.find("{server_time}") >= 0
210def log_response(
211 message,
212 *args,
213 response=None,
214 request=None,
215 logger=request_logger,
216 level=None,
217 exc_info=None,
218):
219 """
220 Log errors based on HttpResponse status.
222 Log 5xx responses as errors and 4xx responses as warnings (unless a level
223 is given as a keyword argument). The HttpResponse status_code and the
224 request are passed to the logger's extra parameter.
225 """
226 # Check if the response has already been logged. Multiple requests to log
227 # the same response can be received in some cases, e.g., when the
228 # response is the result of an exception and is logged at the time the
229 # exception is caught so that the exc_info can be recorded.
230 if getattr(response, "_has_been_logged", False): 230 ↛ 231line 230 didn't jump to line 231, because the condition on line 230 was never true
231 return
233 if level is None: 233 ↛ 241line 233 didn't jump to line 241, because the condition on line 233 was never false
234 if response.status_code >= 500: 234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true
235 level = "error"
236 elif response.status_code >= 400: 236 ↛ 239line 236 didn't jump to line 239, because the condition on line 236 was never false
237 level = "warning"
238 else:
239 level = "info"
241 getattr(logger, level)(
242 message,
243 *args,
244 extra={
245 "status_code": response.status_code,
246 "request": request,
247 },
248 exc_info=exc_info,
249 )
250 response._has_been_logged = True