From f3155973f6f9620f8147243f53917a08e9bfd369 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Jul 2025 15:26:16 -0400 Subject: [PATCH] Subclass django_prometheus middleware to capture REST API metrics --- netbox/netbox/metrics.py | 32 ++++++++++++++++++++++++++++++++ netbox/netbox/middleware.py | 24 ++++++++++++++++++++++++ netbox/netbox/settings.py | 4 ++-- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 netbox/netbox/metrics.py diff --git a/netbox/netbox/metrics.py b/netbox/netbox/metrics.py new file mode 100644 index 000000000..43b549d7c --- /dev/null +++ b/netbox/netbox/metrics.py @@ -0,0 +1,32 @@ +from django_prometheus.conf import NAMESPACE +from django_prometheus import middleware +from prometheus_client import Counter + +__all__ = ( + 'Metrics', +) + + +class Metrics(middleware.Metrics): + """ + Expand the stock Metrics class from django_prometheus to add our own counters. + """ + + def register(self): + super().register() + + # REST API metrics + self.rest_api_requests = self.register_metric( + Counter, + "rest_api_requests_total_by_method", + "Count of total REST API requests by method", + ["method"], + namespace=NAMESPACE, + ) + self.rest_api_requests_by_view_method = self.register_metric( + Counter, + "django_http_requests_total_by_view_method", + "Count of REST API requests by view & method", + ["view", "method"], + namespace=NAMESPACE, + ) diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index 145141615..e4c57d4c1 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -8,8 +8,10 @@ from django.core.exceptions import ImproperlyConfigured from django.db import connection, ProgrammingError from django.db.utils import InternalError from django.http import Http404, HttpResponseRedirect +from django_prometheus import middleware from netbox.config import clear_config, get_config +from netbox.metrics import Metrics from netbox.views import handler_500 from utilities.api import is_api_request from utilities.error_handlers import handle_rest_api_exception @@ -18,6 +20,8 @@ from utilities.request import apply_request_processors __all__ = ( 'CoreMiddleware', 'MaintenanceModeMiddleware', + 'PrometheusAfterMiddleware', + 'PrometheusBeforeMiddleware', 'RemoteUserMiddleware', ) @@ -180,6 +184,26 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): return groups +class PrometheusBeforeMiddleware(middleware.PrometheusBeforeMiddleware): + metrics_cls = Metrics + + +class PrometheusAfterMiddleware(middleware.PrometheusAfterMiddleware): + metrics_cls = Metrics + + def process_response(self, request, response): + response = super().process_response(request, response) + + # Increment REST API request counters + if is_api_request(request): + method = self._method(request) + name = self._get_view_name(request) + self.label_metric(self.metrics.rest_api_requests, request, method=method).inc() + self.label_metric(self.metrics.rest_api_requests_by_view_method, request, method=method, view=name).inc() + + return response + + class MaintenanceModeMiddleware: """ Middleware that checks if the application is in maintenance mode diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index d293a9979..909a67f60 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -471,9 +471,9 @@ if DEBUG: if METRICS_ENABLED: # If metrics are enabled, add the before & after Prometheus middleware MIDDLEWARE = [ - 'django_prometheus.middleware.PrometheusBeforeMiddleware', + 'netbox.middleware.PrometheusBeforeMiddleware', *MIDDLEWARE, - 'django_prometheus.middleware.PrometheusAfterMiddleware', + 'netbox.middleware.PrometheusAfterMiddleware', ] # URLs