Add Prometheus metrics endpoint for KYC monitoring
All checks were successful
Build Docker Image / build (push) Successful in 4m13s

This commit is contained in:
Ruslan Bakiev
2026-02-03 10:32:44 +07:00
parent 10f5c609de
commit 938e291268
2 changed files with 44 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from kyc_app.views import UserGraphQLView, M2MGraphQLView, PublicGraphQLView from kyc_app.views import UserGraphQLView, M2MGraphQLView, PublicGraphQLView, metrics_view
from kyc_app.schemas.user_schema import user_schema from kyc_app.schemas.user_schema import user_schema
from kyc_app.schemas.m2m_schema import m2m_schema from kyc_app.schemas.m2m_schema import m2m_schema
from kyc_app.schemas.public_schema import public_schema from kyc_app.schemas.public_schema import public_schema
@@ -11,4 +11,5 @@ urlpatterns = [
path('graphql/user/', csrf_exempt(UserGraphQLView.as_view(graphiql=True, schema=user_schema))), path('graphql/user/', csrf_exempt(UserGraphQLView.as_view(graphiql=True, schema=user_schema))),
path('graphql/m2m/', csrf_exempt(M2MGraphQLView.as_view(graphiql=True, schema=m2m_schema))), path('graphql/m2m/', csrf_exempt(M2MGraphQLView.as_view(graphiql=True, schema=m2m_schema))),
path('graphql/public/', csrf_exempt(PublicGraphQLView.as_view(graphiql=True, schema=public_schema))), path('graphql/public/', csrf_exempt(PublicGraphQLView.as_view(graphiql=True, schema=public_schema))),
path('metrics', metrics_view),
] ]

View File

@@ -3,9 +3,16 @@ Views for KYC API.
Authentication is handled by GRAPHENE MIDDLEWARE in settings.py Authentication is handled by GRAPHENE MIDDLEWARE in settings.py
""" """
import os
from datetime import timedelta
from django.db.models import Count, Q
from django.http import HttpResponse
from django.utils import timezone
from graphene_django.views import GraphQLView from graphene_django.views import GraphQLView
from .graphql_middleware import UserJWTMiddleware from .graphql_middleware import UserJWTMiddleware
from .models import KYCProfile
class UserGraphQLView(GraphQLView): class UserGraphQLView(GraphQLView):
@@ -47,3 +54,37 @@ class PublicGraphQLView(GraphQLView):
# Use optional auth middleware that doesn't fail on missing token # Use optional auth middleware that doesn't fail on missing token
kwargs['middleware'] = [OptionalUserJWTMiddleware()] kwargs['middleware'] = [OptionalUserJWTMiddleware()]
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def metrics_view(request):
"""Prometheus metrics endpoint for KYC monitoring status."""
stale_days = int(os.getenv("KYC_STALE_DAYS", "90"))
cutoff = timezone.now() - timedelta(days=stale_days)
stats = KYCProfile.objects.filter(workflow_status="active").aggregate(
total=Count("id"),
stale=Count("id", filter=Q(updated_at__lt=cutoff) | Q(updated_at__isnull=True)),
fresh=Count("id", filter=Q(updated_at__gte=cutoff)),
)
total = int(stats.get("total") or 0)
stale = int(stats.get("stale") or 0)
fresh = int(stats.get("fresh") or 0)
ratio = 0.0 if total == 0 else stale / total
payload = (
"# HELP kyc_monitoring_total Total active profiles in monitoring\n"
"# TYPE kyc_monitoring_total gauge\n"
f"kyc_monitoring_total {total}\n"
"# HELP kyc_monitoring_stale Stale profiles (no refresh within window)\n"
"# TYPE kyc_monitoring_stale gauge\n"
f"kyc_monitoring_stale {stale}\n"
"# HELP kyc_monitoring_fresh Fresh profiles (refreshed within window)\n"
"# TYPE kyc_monitoring_fresh gauge\n"
f"kyc_monitoring_fresh {fresh}\n"
"# HELP kyc_monitoring_stale_ratio Stale / total ratio\n"
"# TYPE kyc_monitoring_stale_ratio gauge\n"
f"kyc_monitoring_stale_ratio {ratio}\n"
)
return HttpResponse(payload, content_type="text/plain; version=0.0.4; charset=utf-8")