|
1 | 1 | """Base Django settings for the python.org project.""" |
2 | 2 |
|
| 3 | +import logging |
3 | 4 | from pathlib import Path |
4 | 5 |
|
5 | 6 | from csp.constants import NONCE, SELF, UNSAFE_INLINE |
6 | 7 | from decouple import config |
7 | 8 | from dj_database_url import parse as dj_database_url_parser |
8 | 9 | from django.contrib.messages import constants |
9 | 10 |
|
| 11 | +from pydotorg.logs import configure_structlog |
10 | 12 | from pydotorg.markup import MARKUP_FIELD_TYPES # noqa: F401 - read by django-markupfield via settings |
11 | 13 | from pydotorg.settings.pipeline import PIPELINE # noqa: F401 - accessed by django-pipeline via settings |
12 | 14 |
|
|
158 | 160 | "pydotorg.middleware.AdminNoCaching", |
159 | 161 | "pydotorg.middleware.GlobalSurrogateKey", |
160 | 162 | "django.contrib.auth.middleware.AuthenticationMiddleware", |
| 163 | + "django_structlog.middlewares.RequestMiddleware", |
161 | 164 | "django.contrib.messages.middleware.MessageMiddleware", |
162 | 165 | "waffle.middleware.WaffleMiddleware", |
163 | 166 | "django.middleware.clickjacking.XFrameOptionsMiddleware", |
|
186 | 189 | "django.contrib.admin", |
187 | 190 | "django.contrib.admindocs", |
188 | 191 | "django_celery_beat", |
| 192 | + "django_structlog", |
189 | 193 | "django_translation_aliases", |
190 | 194 | "pipeline", |
191 | 195 | "sitetree", |
|
239 | 243 | "version": 1, |
240 | 244 | "disable_existing_loggers": False, |
241 | 245 | "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, |
| 246 | + "formatters": { |
| 247 | + "console": {"()": "pydotorg.logs.console_formatter"}, |
| 248 | + "json": {"()": "pydotorg.logs.json_formatter"}, |
| 249 | + }, |
242 | 250 | "handlers": { |
| 251 | + "console": { |
| 252 | + "class": "logging.StreamHandler", |
| 253 | + "stream": "ext://sys.stdout", |
| 254 | + "formatter": "console", |
| 255 | + }, |
243 | 256 | "mail_admins": { |
244 | 257 | "level": "ERROR", |
245 | 258 | "filters": ["require_debug_false"], |
246 | 259 | "class": "django.utils.log.AdminEmailHandler", |
247 | | - } |
| 260 | + }, |
248 | 261 | }, |
| 262 | + "root": {"handlers": ["console"], "level": "WARNING"}, |
249 | 263 | "loggers": { |
| 264 | + # Replaces Django's default console handler so records reach stdout once, via root. |
| 265 | + "django": {"handlers": ["mail_admins"], "level": "INFO"}, |
| 266 | + # Emails only; django-structlog's request_failed puts the traceback on stdout with request context. |
250 | 267 | "django.request": { |
251 | 268 | "handlers": ["mail_admins"], |
252 | 269 | "level": "ERROR", |
253 | | - "propagate": True, |
| 270 | + "propagate": False, |
254 | 271 | }, |
| 272 | + "django_structlog": {"level": "INFO"}, |
| 273 | + "apps": {"level": "INFO"}, |
| 274 | + "pydotorg": {"level": "INFO"}, |
| 275 | + "celery": {"level": "INFO"}, |
255 | 276 | }, |
256 | 277 | } |
257 | 278 |
|
| 279 | +configure_structlog() |
| 280 | + |
| 281 | +# nginx and gunicorn already log every request; keep only 5xx and request_failed from django-structlog. |
| 282 | +DJANGO_STRUCTLOG_STATUS_DEFAULT_LOG_LEVEL = logging.DEBUG |
| 283 | +DJANGO_STRUCTLOG_STATUS_4XX_LOG_LEVEL = logging.DEBUG |
| 284 | +# Client IPs were never logged; keep it that way. |
| 285 | +DJANGO_STRUCTLOG_IP_LOGGING_ENABLED = False |
| 286 | + |
258 | 287 | ### Honeypot |
259 | 288 | HONEYPOT_FIELD_NAME = "email_body_text" |
260 | 289 | HONEYPOT_VALUE = "write your message" |
|
0 commit comments