From 2e5e3b0a26cade5b57b61adfe828d46cf058d499 Mon Sep 17 00:00:00 2001 From: pierregeeraert Date: Mon, 3 Aug 2026 14:22:48 +0100 Subject: [PATCH] trying the netdata logging --- django_code/apps/netdata_logging/__init__.py | 0 django_code/apps/netdata_logging/apps.py | 7 +++ .../apps/netdata_logging/credentials.py | 23 ++++++++++ django_code/apps/netdata_logging/logger.py | 44 +++++++++++++++++++ django_code/apps/netdata_logging/views.py | 8 ++++ django_code/core/settings.py | 1 + django_code/core/urls.py | 3 ++ requirements.txt | 1 + 8 files changed, 87 insertions(+) create mode 100644 django_code/apps/netdata_logging/__init__.py create mode 100644 django_code/apps/netdata_logging/apps.py create mode 100644 django_code/apps/netdata_logging/credentials.py create mode 100644 django_code/apps/netdata_logging/logger.py create mode 100644 django_code/apps/netdata_logging/views.py diff --git a/django_code/apps/netdata_logging/__init__.py b/django_code/apps/netdata_logging/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_code/apps/netdata_logging/apps.py b/django_code/apps/netdata_logging/apps.py new file mode 100644 index 0000000..e450ec8 --- /dev/null +++ b/django_code/apps/netdata_logging/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class NetdataLoggingConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.netdata_logging' + label = 'netdata_logging' diff --git a/django_code/apps/netdata_logging/credentials.py b/django_code/apps/netdata_logging/credentials.py new file mode 100644 index 0000000..3594c27 --- /dev/null +++ b/django_code/apps/netdata_logging/credentials.py @@ -0,0 +1,23 @@ +# ===================================================================== +# NETDATA CONNECTION CONFIG - TEMPORARY / HARDCODED +# +# These values are kept in plaintext here ONLY until they are moved into +# the secret manager (see apps/bitwarden). Treat everything below as +# something that must be migrated before this app is used beyond a local +# hello-world test. +# +# Current status: logger.py logs structured JSON to stdout, which is the +# supported way for Netdata's log collectors (systemd-journal / generic +# log source) to pick up application logs - it does NOT push over the +# network, so `room_token` below is not consumed by any code yet. +# +# `agent_host` / `agent_port` point at the Netdata Agent's dashboard/API +# port (read-only, no push support). They're recorded here so the values +# are in one obvious place, ready to be reused once a real push mechanism +# (StatsD or an OpenTelemetry log receiver, on their own ports - NOT +# agent_port) is confirmed enabled on this agent. +# ===================================================================== +class netdata: + agent_host = "netdata-agent.netdata.svc" + agent_port = 19999 # Netdata Agent dashboard/API port - read-only, not used to push logs + room_token = "" # Netdata Cloud room token - NOT currently used by any code in this app diff --git a/django_code/apps/netdata_logging/logger.py b/django_code/apps/netdata_logging/logger.py new file mode 100644 index 0000000..bf2dd7b --- /dev/null +++ b/django_code/apps/netdata_logging/logger.py @@ -0,0 +1,44 @@ +""" +Structured logging helper for Netdata. + +Netdata does not expose a generic "push a log line" API reachable with +just a room token from outside an Agent (see credentials.py). Until a +real push mechanism (StatsD or an OpenTelemetry log receiver) is +confirmed enabled on the agent, the supported path is the standard one: +emit structured JSON lines to stdout, which Netdata's log collectors +(systemd-journal / generic log source) are built to tail and parse. +""" +import logging +import sys + +from pythonjsonlogger import jsonlogger + +from .credentials import netdata + +_LOGGER_NAME = "netdata" + + +def get_netdata_logger() -> logging.Logger: + logger = logging.getLogger(_LOGGER_NAME) + + if not logger.handlers: + handler = logging.StreamHandler(stream=sys.stdout) + handler.setFormatter( + jsonlogger.JsonFormatter("%(asctime)s %(levelname)s %(name)s %(message)s") + ) + logger.addHandler(handler) + logger.setLevel(logging.INFO) + logger.propagate = False + + return logger + + +def log_event(message, level="info", **fields): + """Log a structured event intended to be picked up by Netdata. + + Extra keyword arguments are attached as structured fields on the JSON + log line, e.g. log_event("hello world", source="netdata_logging.hello"). + """ + logger = get_netdata_logger() + log_fn = getattr(logger, level.lower(), logger.info) + log_fn(message, extra={"netdata_agent_host": netdata.agent_host, **fields}) diff --git a/django_code/apps/netdata_logging/views.py b/django_code/apps/netdata_logging/views.py new file mode 100644 index 0000000..1cb4dce --- /dev/null +++ b/django_code/apps/netdata_logging/views.py @@ -0,0 +1,8 @@ +from django.http import HttpResponse + +from .logger import log_event + + +def hello(request): + log_event("Hello world from netdata_logging!", source="netdata_logging.hello") + return HttpResponse('

Hello Netdata logging!

') diff --git a/django_code/core/settings.py b/django_code/core/settings.py index e9ff76f..127fba5 100644 --- a/django_code/core/settings.py +++ b/django_code/core/settings.py @@ -49,6 +49,7 @@ INSTALLED_APPS = [ 'apps.job_dashboard.apps.JobDashboardConfig', 'apps.telegram_communication.apps.TelegramCommunicationConfig', 'apps.podcast_aggregator.apps.PodcastAggregatorConfig', + 'apps.netdata_logging.apps.NetdataLoggingConfig', 'import_export', 'djangoql', ] diff --git a/django_code/core/urls.py b/django_code/core/urls.py index c8bb224..9307f6a 100644 --- a/django_code/core/urls.py +++ b/django_code/core/urls.py @@ -27,6 +27,7 @@ from apps.stash_management import apps as stashapp from apps.stash_management import views as stashview from apps.website_redirection import views as website_redirectionapp from apps.podcast_aggregator import views as podcast_views +from apps.netdata_logging import views as netdata_logging_views urlpatterns = [ @@ -93,5 +94,7 @@ urlpatterns = [ ################### cloudmanager ################### path('podcast/', podcast_views.execute_podcast), + ################### netdata_logging ################### + path('netdata/hello/', netdata_logging_views.hello), ] diff --git a/requirements.txt b/requirements.txt index 137d398..6bd0bf5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ bitwarden_sdk xmltodict whitenoise djangoql +python-json-logger