mirror of
https://github.com/netbox-community/netbox.git
synced 2025-09-06 14:23:36 -06:00
parent
9a2fab1d48
commit
ae55eed98f
@ -11,6 +11,8 @@ NetBox makes use of the [django-prometheus](https://github.com/korfuri/django-pr
|
|||||||
- Per model insert, update, and delete counters
|
- Per model insert, update, and delete counters
|
||||||
- Per view request counters
|
- Per view request counters
|
||||||
- Per view request latency histograms
|
- Per view request latency histograms
|
||||||
|
- REST API requests (by endpoint & method)
|
||||||
|
- GraphQL API requests
|
||||||
- Request body size histograms
|
- Request body size histograms
|
||||||
- Response body size histograms
|
- Response body size histograms
|
||||||
- Response code counters
|
- Response code counters
|
||||||
|
40
netbox/netbox/metrics.py
Normal file
40
netbox/netbox/metrics.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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,
|
||||||
|
"rest_api_requests_total_by_view_method",
|
||||||
|
"Count of REST API requests by view & method",
|
||||||
|
["view", "method"],
|
||||||
|
namespace=NAMESPACE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# GraphQL API metrics
|
||||||
|
self.graphql_api_requests = self.register_metric(
|
||||||
|
Counter,
|
||||||
|
"graphql_api_requests_total",
|
||||||
|
"Count of total GraphQL API requests",
|
||||||
|
namespace=NAMESPACE,
|
||||||
|
)
|
@ -8,16 +8,20 @@ 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, is_graphql_request
|
||||||
from utilities.error_handlers import handle_rest_api_exception
|
from utilities.error_handlers import handle_rest_api_exception
|
||||||
from utilities.request import apply_request_processors
|
from utilities.request import apply_request_processors
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'CoreMiddleware',
|
'CoreMiddleware',
|
||||||
'MaintenanceModeMiddleware',
|
'MaintenanceModeMiddleware',
|
||||||
|
'PrometheusAfterMiddleware',
|
||||||
|
'PrometheusBeforeMiddleware',
|
||||||
'RemoteUserMiddleware',
|
'RemoteUserMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -180,6 +184,30 @@ 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()
|
||||||
|
|
||||||
|
# Increment GraphQL API request counters
|
||||||
|
elif is_graphql_request(request):
|
||||||
|
self.metrics.graphql_api_requests.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
|
||||||
|
@ -472,9 +472,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
|
||||||
|
@ -23,6 +23,7 @@ __all__ = (
|
|||||||
'get_serializer_for_model',
|
'get_serializer_for_model',
|
||||||
'get_view_name',
|
'get_view_name',
|
||||||
'is_api_request',
|
'is_api_request',
|
||||||
|
'is_graphql_request',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -60,6 +61,13 @@ def is_api_request(request):
|
|||||||
return request.path_info.startswith(api_path) and request.content_type == HTTP_CONTENT_TYPE_JSON
|
return request.path_info.startswith(api_path) and request.content_type == HTTP_CONTENT_TYPE_JSON
|
||||||
|
|
||||||
|
|
||||||
|
def is_graphql_request(request):
|
||||||
|
"""
|
||||||
|
Return True of the request is being made via the GraphQL API.
|
||||||
|
"""
|
||||||
|
return request.path_info == reverse('graphql') and request.content_type == HTTP_CONTENT_TYPE_JSON
|
||||||
|
|
||||||
|
|
||||||
def get_view_name(view):
|
def get_view_name(view):
|
||||||
"""
|
"""
|
||||||
Derive the view name from its associated model, if it has one. Fall back to DRF's built-in `get_view_name()`.
|
Derive the view name from its associated model, if it has one. Fall back to DRF's built-in `get_view_name()`.
|
||||||
|
Loading…
Reference in New Issue
Block a user