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

1import logging 

2import logging.config # needed when logging_config doesn't start with logging.config 

3from copy import copy 

4 

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 

10 

11request_logger = logging.getLogger("django.request") 

12 

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} 

65 

66 

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) 

71 

72 logging.config.dictConfig(DEFAULT_LOGGING) 

73 

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) 

77 

78 

79class AdminEmailHandler(logging.Handler): 

80 """An exception log handler that emails log entries to site admins. 

81 

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 """ 

85 

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 ) 

93 

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) 

110 

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 

116 

117 if record.exc_info: 

118 exc_info = record.exc_info 

119 else: 

120 exc_info = (None, record.getMessage(), None) 

121 

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) 

129 

130 def send_mail(self, subject, message, *args, **kwargs): 

131 mail.mail_admins( 

132 subject, message, *args, connection=self.connection(), **kwargs 

133 ) 

134 

135 def connection(self): 

136 return get_connection(backend=self.email_backend, fail_silently=True) 

137 

138 def format_subject(self, subject): 

139 """ 

140 Escape CR and LF characters. 

141 """ 

142 return subject.replace("\n", "\\n").replace("\r", "\\r") 

143 

144 

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 """ 

151 

152 def __init__(self, callback): 

153 self.callback = callback 

154 

155 def filter(self, record): 

156 if self.callback(record): 

157 return 1 

158 return 0 

159 

160 

161class RequireDebugFalse(logging.Filter): 

162 def filter(self, record): 

163 return not settings.DEBUG 

164 

165 

166class RequireDebugTrue(logging.Filter): 

167 def filter(self, record): 

168 return settings.DEBUG 

169 

170 

171class ServerFormatter(logging.Formatter): 

172 default_time_format = "%d/%b/%Y %H:%M:%S" 

173 

174 def __init__(self, *args, **kwargs): 

175 self.style = color_style() 

176 super().__init__(*args, **kwargs) 

177 

178 def format(self, record): 

179 msg = record.msg 

180 status_code = getattr(record, "status_code", None) 

181 

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) 

199 

200 if self.uses_server_time() and not hasattr(record, "server_time"): 

201 record.server_time = self.formatTime(record, self.datefmt) 

202 

203 record.msg = msg 

204 return super().format(record) 

205 

206 def uses_server_time(self): 

207 return self._fmt.find("{server_time}") >= 0 

208 

209 

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. 

221 

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 

232 

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" 

240 

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