Configure logging

This commit is contained in:
Daniel Egger 2022-02-08 18:50:56 +01:00
parent 0ce5c93b9b
commit b5a243b141
10 changed files with 81 additions and 23 deletions

View File

@ -6,4 +6,3 @@ docker push iterativ/vbv-lernwelt-django
# deploy to caprover
caprover deploy -h https://captain.control.iterativ.ch -a vbv-lernwelt -i docker.io/iterativ/vbv-lernwelt-django

View File

@ -7,7 +7,7 @@ from pathlib import Path
import structlog
from environs import Env
from vbv_lernwelt.core.utils import add_app_info
from vbv_lernwelt.core.utils import structlog_add_app_info
SERVER_ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
# vbv_lernwelt/
@ -135,6 +135,7 @@ AUTH_PASSWORD_VALIDATORS = [
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
MIDDLEWARE = [
"vbv_lernwelt.core.middleware.security.GetIpBehindReverseProxyMiddleWare",
"django.middleware.security.SecurityMiddleware",
"corsheaders.middleware.CorsMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
@ -248,10 +249,6 @@ VBV_DJANGO_LOGGING_CONF = env(
if VBV_DJANGO_LOGGING_CONF == "VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S")
pre_chain = [
structlog.stdlib.add_log_level,
timestamper,
]
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
@ -259,7 +256,10 @@ if VBV_DJANGO_LOGGING_CONF == "VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
"colored": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(colors=True),
"foreign_pre_chain": pre_chain,
"foreign_pre_chain": [
structlog.stdlib.add_log_level,
timestamper,
],
},
},
"handlers": {
@ -298,14 +298,25 @@ if VBV_DJANGO_LOGGING_CONF == "VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
cache_logger_on_first_use=True,
)
else:
shared_processors = [
structlog.threadlocal.merge_threadlocal,
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog_add_app_info,
structlog.processors.TimeStamper(fmt="iso"),
]
LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"format": "%(asctime)s %(msecs)03d %(process)d %(thread)d %(levelname)s %(name)s %(filename)s %(lineno)d %(funcName)s %(message)s", # noqa I004
"datefmt": "%Y-%m-%dT%H:%M:%S",
"()": structlog.stdlib.ProcessorFormatter,
"processors": [
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
structlog.processors.JSONRenderer()
],
"foreign_pre_chain": shared_processors,
},
},
"handlers": {
@ -332,7 +343,7 @@ else:
"level": "WARNING",
"propagate": False,
},
"vbc_lernwelt": {
"vbv_lernwelt": {
"handlers": ["console", "file"],
"level": "DEBUG",
"propagate": False,
@ -346,16 +357,7 @@ else:
}
structlog.configure(
processors=[
structlog.threadlocal.merge_threadlocal,
structlog.stdlib.filter_by_level,
add_app_info,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.render_to_log_kwargs,
],
processors=shared_processors + [structlog.stdlib.ProcessorFormatter.wrap_for_formatter,],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,

View File

@ -1,3 +1,3 @@
export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt'
export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR
#export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR
export VBV_DJANGO_DEBUG=True

View File

View File

@ -89,6 +89,8 @@ django-extensions==3.1.5
# via -r requirements-dev.in
django-htmx==1.8.0
# via -r requirements.in
django-ipware==4.0.2
# via -r requirements.in
django-model-utils==4.2.0
# via -r requirements.in
django-ratelimit==3.0.1

View File

@ -22,6 +22,7 @@ django-htmx
dj-database-url
django-click
django-ratelimit
django-ipware
psycopg2-binary
gunicorn

View File

@ -43,6 +43,8 @@ django-cors-headers==3.11.0
# via -r requirements.in
django-htmx==1.8.0
# via -r requirements.in
django-ipware==4.0.2
# via -r requirements.in
django-model-utils==4.2.0
# via -r requirements.in
django-ratelimit==3.0.1

View File

@ -1,6 +1,9 @@
import uuid
import structlog
from django.core.exceptions import PermissionDenied
from django.http import Http404
from ipware import get_client_ip
from structlog.threadlocal import bind_threadlocal, clear_threadlocal
from vbv_lernwelt.core.models import SecurityRequestResponseLog
@ -8,6 +11,19 @@ from vbv_lernwelt.core.models import SecurityRequestResponseLog
logger = structlog.get_logger(__name__)
class GetIpBehindReverseProxyMiddleWare:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
client_ip, _is_routable = get_client_ip(request)
request.META['REMOTE_ADDR'] = client_ip
response = self.get_response(request)
return response
class SecurityRequestResponseLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
@ -74,3 +90,19 @@ class SecurityRequestResponseLoggingMiddleware:
def __call__(self, request):
return self.log_request_response(request)
def process_exception(self, request, exception):
if isinstance(exception, (Http404, PermissionDenied)):
# We don't log an exception here, and we don't set that we handled
# an error as we want the standard `request_finished` log message
# to be emitted.
return
self._raised_exception = True
self.bind_user_id(request),
logger.exception(
"request_failed",
code=500,
request=self.format_request(request),
)

View File

@ -1,11 +1,12 @@
import logging
import structlog
from django.conf import settings
from rest_framework.throttling import UserRateThrottle
from structlog.types import EventDict
def add_app_info(
def structlog_add_app_info(
logger: logging.Logger, method_name: str, event_dict: EventDict
) -> EventDict:
event_dict["django_app"] = "vbv_lernwelt"
@ -15,6 +16,20 @@ def add_app_info(
return event_dict
def structlog_inject_context_dict(test, level, event_dict):
"""
Add the structlog context dict to log events generated by the stdlib logging library.
"""
context_class = structlog.get_config().get('context_class')
if context_class:
for key, value in context_class().items():
if key not in event_dict:
event_dict[key] = value
return event_dict
class HourUserRateThrottle(UserRateThrottle):
scope = "hour-throttle"

View File

@ -1,8 +1,11 @@
import structlog
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from vbv_lernwelt.simpletodo.models import SimpleTask, SimpleList
logger = structlog.get_logger(__name__)
class SimpleTaskSerializer(ModelSerializer):
list_title = serializers.CharField(max_length=100)
@ -27,4 +30,6 @@ class SimpleTaskSerializer(ModelSerializer):
simple_list, _ = SimpleList.objects.get_or_create(title=list_title, user=user)
validated_data["list"] = simple_list
logger.debug("Creating task", title=validated_data.get('title'), list_title=list_title)
return super().create(validated_data)