From 86ccdd58ee03b453d0a1fb6dec8eb6818101b305 Mon Sep 17 00:00:00 2001 From: bronsen <kontakt+gitcommit@nrrd.de> Date: Sat, 15 Mar 2025 21:57:13 +0100 Subject: [PATCH] [logs] set up and use structlog in our app --- collector/views.py | 8 ++++--- config/settings.py | 54 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/collector/views.py b/collector/views.py index cc6023d..13d7a09 100644 --- a/collector/views.py +++ b/collector/views.py @@ -1,4 +1,4 @@ -import logging +import structlog from typing import Any from django.db import transaction @@ -9,7 +9,7 @@ from django.views import generic from .models import Teil -logger = logging.getLogger(__name__) +logger = structlog.get_logger(__name__) DEFAULT_TEILE_NUMBER = 10 @@ -47,8 +47,10 @@ class DetailView(generic.DetailView): def enter(request: HttpRequest) -> HttpResponse: try: with transaction.atomic(): - Teil.objects.create(name=request.POST["new_name"]) + teil = Teil.objects.create(name=request.POST["new_name"]) except Exception: logger.warning("Teil already existed") + else: + logger.info("New Teil entered", teil_id=teil.pk) return HttpResponseRedirect(reverse("collector:list")) diff --git a/config/settings.py b/config/settings.py index e9c707c..febcbb7 100644 --- a/config/settings.py +++ b/config/settings.py @@ -8,9 +8,12 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ -import environ +import os from pathlib import Path +import environ +import structlog + BASE_DIR = Path(__file__).resolve().parent.parent env = environ.Env( @@ -32,6 +35,7 @@ CSRF_COOKIE_SECURE = True # Application definition INSTALLED_APPS = [ + "django_structlog", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -48,6 +52,7 @@ if DEPLOYMENT == "DEV": SHELL_PLUS = "ipython" MIDDLEWARE = [ + "django_structlog.middlewares.RequestMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -104,3 +109,50 @@ STATIC_URL = "static/" # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +LOGGING = { + "version": 1, + "disable_existing_loggers": True, + "formatters": { + "logftm_formatter": { + "()": structlog.stdlib.ProcessorFormatter, + "processor": structlog.processors.LogfmtRenderer(sort_keys=True, key_order=["level", "event"]), + }, + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "logftm_formatter", + }, + }, + "loggers": { + "django_structlog": { + "handlers": ["console"], + "level": "DEBUG", + }, + "root": { + "handlers": ["console"], + "level": "DEBUG", + }, + }, +} + +structlog.configure( + processors=[ + structlog.contextvars.merge_contextvars, + structlog.stdlib.filter_by_level, + structlog.processors.TimeStamper(fmt="iso"), + structlog.stdlib.add_logger_name, + structlog.stdlib.add_log_level, + structlog.stdlib.PositionalArgumentsFormatter(), + structlog.processors.StackInfoRenderer(), + structlog.processors.format_exc_info, + structlog.processors.UnicodeDecoder(), + structlog.stdlib.ProcessorFormatter.wrap_for_formatter, + ], + logger_factory=structlog.stdlib.LoggerFactory(), + cache_logger_on_first_use=True, +) +os.makedirs(BASE_DIR / "logs", exist_ok=True) + +# DJANGO_STRUCTLOG_COMMAND_LOGGING_ENABLED = True