diff --git a/doc/admin/config.rst b/doc/admin/config.rst index b26ed7ae30..c5e6f84093 100644 --- a/doc/admin/config.rst +++ b/doc/admin/config.rst @@ -117,6 +117,9 @@ Example:: ``loglevel`` Set console and file log level (``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` or ``CRITICAL``). Defaults to ``INFO``. +``request_id_header`` + Specifies the name of a header that should be used for logging request IDs. Off by default. + Locale settings --------------- diff --git a/src/pretix/helpers/logs.py b/src/pretix/helpers/logs.py index b3fc9fcdb3..e1e322322f 100644 --- a/src/pretix/helpers/logs.py +++ b/src/pretix/helpers/logs.py @@ -21,9 +21,49 @@ # import logging +import sentry_sdk +from django.core.signals import request_finished +from django.dispatch import receiver + +try: + from asgiref.local import Local +except ImportError: + from threading import local as Local + from django.conf import settings class AdminExistsFilter(logging.Filter): def filter(self, record): return not settings.DEBUG and len(settings.ADMINS) > 0 + + +local = Local() + + +class RequestIdFilter(logging.Filter): + def filter(self, record): + record.request_id = getattr(local, 'request_id', None) + return True + + +class RequestIdMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if settings.REQUEST_ID_HEADER and settings.REQUEST_ID_HEADER in request.headers: + local.request_id = request.request_id = request.headers[settings.REQUEST_ID_HEADER] + + if settings.SENTRY_ENABLED: + sentry_sdk.set_tag("request_id", request.request_id) + else: + local.request_id = request.request_id = None + + return self.get_response(request) + + +@receiver(request_finished) +def on_request_finished(sender, **kwargs): + # not part of middleware, since things could be logged after the middleware stack is finished + local.request_id = None diff --git a/src/pretix/settings.py b/src/pretix/settings.py index f58bd0d532..ce2041ec3d 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -178,6 +178,8 @@ CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).hostname] TRUST_X_FORWARDED_FOR = config.get('pretix', 'trust_x_forwarded_for', fallback=False) +REQUEST_ID_HEADER = config.get('pretix', 'request_id_header', fallback=False) + if config.get('pretix', 'trust_x_forwarded_proto', fallback=False): SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') @@ -439,6 +441,7 @@ CORE_MODULES = { } MIDDLEWARE = [ + 'pretix.helpers.logs.RequestIdMiddleware', 'pretix.api.middleware.IdempotencyMiddleware', 'pretix.multidomain.middlewares.MultiDomainMiddleware', 'pretix.base.middleware.CustomCommonMiddleware', @@ -684,31 +687,41 @@ LOGGING = { 'disable_existing_loggers': False, 'formatters': { 'default': { - 'format': '%(levelname)s %(asctime)s %(name)s %(module)s %(message)s' + 'format': ( + '%(levelname)s %(asctime)s RequestId=%(request_id)s %(name)s %(module)s %(message)s' + if REQUEST_ID_HEADER + else '%(levelname)s %(asctime)s %(name)s %(module)s %(message)s' + ) }, }, 'filters': { 'require_admin_enabled': { '()': 'pretix.helpers.logs.AdminExistsFilter', - } + }, + 'request_id': { + '()': 'pretix.helpers.logs.RequestIdFilter' + }, }, 'handlers': { 'console': { 'level': loglevel, 'class': 'logging.StreamHandler', - 'formatter': 'default' + 'formatter': 'default', + 'filters': ['request_id'], }, 'csp_file': { 'level': loglevel, 'class': 'logging.FileHandler', 'filename': os.path.join(LOG_DIR, 'csp.log'), - 'formatter': 'default' + 'formatter': 'default', + 'filters': ['request_id'], }, 'file': { 'level': loglevel, 'class': 'logging.FileHandler', 'filename': os.path.join(LOG_DIR, 'pretix.log'), - 'formatter': 'default' + 'formatter': 'default', + 'filters': ['request_id'], }, 'mail_admins': { 'level': 'ERROR',