From 45b99115aefcb76c1e9fd3104c93e14ec539a4a9 Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Wed, 6 May 2026 15:23:57 +0200 Subject: [PATCH 1/4] Add new django setting "ENVIRONMENT" This may be used to enrich for instance logs and is a slug (string without whitespace) that defaults to "environment-unset". It can be overridden via the environment variable "ARGUS_ENVIRONMENT". When running in development this could be set to for instance "dev" or "localhost". When running in production it could be automatically set by the deployment system, to an ip-address or hostname for instance. --- src/argus/site/settings/base.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/argus/site/settings/base.py b/src/argus/site/settings/base.py index 8cb24fe72..d7b18391c 100644 --- a/src/argus/site/settings/base.py +++ b/src/argus/site/settings/base.py @@ -322,6 +322,13 @@ # App settings: override themes, urls, context processors +BANNER_MESSAGE = get_str_env("ARGUS_BANNER_MESSAGE", default=None) + +# Used for looking up the latest version of Argus on PyPI +PYPI_URL = get_str_env("ARGUS_PYPI_URL", "https://pypi-proxy.sokrates.edupaas.no") + +ENVIRONMENT = get_str_env("ARGUS_ENVIRONMENT", "environment-unset") + # add apps that may override other apps _overriding_apps_env = get_json_env("ARGUS_OVERRIDING_APPS", [], quiet=False) OVERRIDING_APPS = validate_app_setting(_overriding_apps_env) @@ -334,7 +341,4 @@ del _extra_apps_env update_settings(globals(), EXTRA_APPS) -BANNER_MESSAGE = get_str_env("ARGUS_BANNER_MESSAGE", default=None) - -# Used for looking up the latest version of Argus on PyPI -PYPI_URL = get_str_env("ARGUS_PYPI_URL", "https://pypi-proxy.sokrates.edupaas.no") +#### DO NOT DEFINE ANY SETTINGS BELOW OVERRIDING_APPS and EXTRA_APPS! From 44fe0eb26c77f4e38c57cb568d28bf57227bd752 Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Thu, 7 May 2026 14:31:53 +0200 Subject: [PATCH 2/4] Make ENVIRONMENT setting available to logs --- src/argus/logging/__init__.py | 8 ++++++++ src/argus/logging/filters.py | 25 ++++++++++++++++++++++++ src/argus/logging/formatters.py | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/argus/logging/__init__.py create mode 100644 src/argus/logging/filters.py create mode 100644 src/argus/logging/formatters.py diff --git a/src/argus/logging/__init__.py b/src/argus/logging/__init__.py new file mode 100644 index 000000000..bc7b412bc --- /dev/null +++ b/src/argus/logging/__init__.py @@ -0,0 +1,8 @@ +# Inspiration: +# +# * https://nhairs.github.io/python-json-logger/latest/cookbook/ +# * https://morethanmonkeys.medium.com/structured-logging-with-python-and-django-from-log-soup-to-useful-events-a8de3003ac87 +# * https://medium.com/@joseph4jubilant/logging-system-in-django-e4c55f624861 +# * https://blog.allegro.tech/2021/06/python-logging.html +# +# Not really a fan of https://docs.python.org/3/howto/logging-cookbook.html#implementing-structured-logging diff --git a/src/argus/logging/filters.py b/src/argus/logging/filters.py new file mode 100644 index 000000000..e6b0163c8 --- /dev/null +++ b/src/argus/logging/filters.py @@ -0,0 +1,25 @@ +import logging + + +__all__ = [ + "EnvironmentFilter", +] + + +class EnvironmentFilter(logging.Filter): + "Attach deployment-specific context to log records" + + def __init__(self, name=""): + super().__init__(name) + + from django import settings + + self.ENVIRONMENT = getattr(settings, "ENVIRONMENT", None) + + def filter(self, record: logging.LogRecord) -> bool: + """ + Modify the LogRecord in place, then return True so the record is logged. + """ + if not hasattr(record, "environment"): + record.environment = self.ENVIRONMENT + return True diff --git a/src/argus/logging/formatters.py b/src/argus/logging/formatters.py new file mode 100644 index 000000000..a440a8f7a --- /dev/null +++ b/src/argus/logging/formatters.py @@ -0,0 +1,34 @@ +from logging import Formatter + +__all__ = [ + "EnvironmentFormatter", + "JSONEnvironmentFormatter", +] + + +class EnvironmentMixin: + def __init__(self, **kwargs): + super().__init__(**kwargs) + from django import settings + + self.ENVIRONMENT = getattr(settings, "ENVIRONMENT", None) + + +class EnvironmentFormatter(EnvironmentMixin, Formatter): + def format(self, record): + if not hasattr(record, "environment"): + record.environment = self.ENVIRONMENT + super().format(record) + + +try: + from pythonjsonlogger.json import JsonFormatter +except ImportError: + JSONEnvironmentFormatter = EnvironmentFormatter + +else: + + class JSONEnvironmentFormatter(EnvironmentMixin, JsonFormatter): + def process_log_record(self, log_data): + if not hasattr(log_data, "environment"): + log_data.environment = self.ENVIRONMENT From 76b8ab51a01504d0d4dbd8b2478eac3dc3e7ac2f Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Thu, 7 May 2026 15:27:09 +0200 Subject: [PATCH 3/4] add docs --- docs/production.rst | 1 + docs/production/logging.rst | 68 +++++++++++++++++++++++ docs/reference/site-specific-settings.rst | 8 +++ 3 files changed, 77 insertions(+) create mode 100644 docs/production/logging.rst diff --git a/docs/production.rst b/docs/production.rst index 8fc5bc10a..c647e75d7 100644 --- a/docs/production.rst +++ b/docs/production.rst @@ -6,3 +6,4 @@ Running in production .. toctree:: production/task-queue + production/logging diff --git a/docs/production/logging.rst b/docs/production/logging.rst new file mode 100644 index 000000000..8e1a1bf3c --- /dev/null +++ b/docs/production/logging.rst @@ -0,0 +1,68 @@ +===================== +Logging in production +===================== + +Logging a deployment-specific environment +========================================= + +Utilizing the setting :setting:`ENVIRONMENT` and either the logging filter +``argus.logging.filters.EnvironmentFilter`` or the formatter +``argus.logging.formatters.EnvironmentFormatter`` it is possible to add +whatever is set in ``ENVIRONMENT`` to every line logged. + +``ENVIRONMENT`` can be set via the environment variable ``ARGUS_ENVIRONMENT``. + +Set it to for instance a hostname or ip address or pod name on deployment, to +make it easy to filter out which logs come from which deployment if all logs +end up at the same place. + +Configure the filter, for instance via the dict method, like so:: + + { + .. + "filters": { + .. + "environment": ["argus.logging.filters.EnvironmentFilter"], + .. + }, + .. + } + +Configure the formatter, for instance via the dict method, like so:: + + { + .. + "formatters": { + .. + "environment": { + "()": "argus.logging.formatters.EnvironmentFormatter", + "format": "{environment} {levelname} {message}", + "style": "{", + }, + .. + }, + .. + } + +Pick one of the above. + +Structured logging with JSON +============================ + +By installing ``python-json-logger`` (for instance via ``pip install +python-json-logger`` or ``pip install ".[jsonlogging]"`` and setting up logging formatters like this: + +:: + + "formatters": { + .. + "json": { + "()": "pythonjsonlogger.json.JsonFormatter", + "format": "asctime,name,funcName,levelname,message", + "style": ",", + }, + .. + }, + +With ``python-json-logger`` installed you can also use the formatter +``argus.logging.formatters.JSONEnvironmentFormatter``. diff --git a/docs/reference/site-specific-settings.rst b/docs/reference/site-specific-settings.rst index 92f38c948..28794a2bd 100644 --- a/docs/reference/site-specific-settings.rst +++ b/docs/reference/site-specific-settings.rst @@ -418,6 +418,14 @@ Special environment settings opacity as a percentage, default ``25%``). See :ref:`themes-and-styling` for how to customize themes. +.. setting:: ENVIRONMENT + +* :setting:`ENVIRONMENT` is a plain text string used to enrich log messages. + Examples: "dev", "production", "new feature test". + +See :doc:`../production/logging` + + Debugging settings ------------------ From 6a8a177e721755873a0e6cd735f8aba1cdd3d3c7 Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Fri, 8 May 2026 11:42:26 +0200 Subject: [PATCH 4/4] add changelog fragment --- changelog.d/+add-environment-info.added.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog.d/+add-environment-info.added.md diff --git a/changelog.d/+add-environment-info.added.md b/changelog.d/+add-environment-info.added.md new file mode 100644 index 000000000..062b6e979 --- /dev/null +++ b/changelog.d/+add-environment-info.added.md @@ -0,0 +1,2 @@ +Add a new setting "ENVIRONMENT" usable for enriching logs. Includes a logging +filter and formatter that adds the environment to each log record.