Subclass django_prometheus middleware to capture REST API metrics

This commit is contained in:
Jeremy Stretch 2025-07-28 15:26:16 -04:00
parent 6b70dea18b
commit f3155973f6
3 changed files with 58 additions and 2 deletions

32
netbox/netbox/metrics.py Normal file
View File

@ -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,
)

View File

@ -8,8 +8,10 @@ from django.core.exceptions import ImproperlyConfigured
from django.db import connection, ProgrammingError from django.db import connection, ProgrammingError
from django.db.utils import InternalError from django.db.utils import InternalError
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django_prometheus import middleware
from netbox.config import clear_config, get_config from netbox.config import clear_config, get_config
from netbox.metrics import Metrics
from netbox.views import handler_500 from netbox.views import handler_500
from utilities.api import is_api_request from utilities.api import is_api_request
from utilities.error_handlers import handle_rest_api_exception from utilities.error_handlers import handle_rest_api_exception
@ -18,6 +20,8 @@ from utilities.request import apply_request_processors
__all__ = ( __all__ = (
'CoreMiddleware', 'CoreMiddleware',
'MaintenanceModeMiddleware', 'MaintenanceModeMiddleware',
'PrometheusAfterMiddleware',
'PrometheusBeforeMiddleware',
'RemoteUserMiddleware', 'RemoteUserMiddleware',
) )
@ -180,6 +184,26 @@ class RemoteUserMiddleware(RemoteUserMiddleware_):
return groups 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: class MaintenanceModeMiddleware:
""" """
Middleware that checks if the application is in maintenance mode Middleware that checks if the application is in maintenance mode

View File

@ -471,9 +471,9 @@ if DEBUG:
if METRICS_ENABLED: if METRICS_ENABLED:
# If metrics are enabled, add the before & after Prometheus middleware # If metrics are enabled, add the before & after Prometheus middleware
MIDDLEWARE = [ MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware', 'netbox.middleware.PrometheusBeforeMiddleware',
*MIDDLEWARE, *MIDDLEWARE,
'django_prometheus.middleware.PrometheusAfterMiddleware', 'netbox.middleware.PrometheusAfterMiddleware',
] ]
# URLs # URLs