44Tracks HTTP requests, response times, and custom application metrics.
55"""
66
7- from prometheus_client import Counter , Histogram , Gauge , generate_latest , CONTENT_TYPE_LATEST
7+ from collections .abc import Callable
8+ from time import time
9+ import logging
10+
11+ from prometheus_client import (
12+ Counter ,
13+ Histogram ,
14+ Gauge ,
15+ generate_latest ,
16+ CONTENT_TYPE_LATEST ,
17+ )
818from starlette .middleware .base import BaseHTTPMiddleware
919from starlette .requests import Request
1020from starlette .responses import Response
11- from time import time
12- import logging
1321
1422logger = logging .getLogger (__name__ )
1523
1624# HTTP Metrics
1725http_requests_total = Counter (
18- ' http_requests_total' ,
19- ' Total HTTP requests' ,
20- [' method' , ' endpoint' , ' status_code' ]
26+ " http_requests_total" ,
27+ " Total HTTP requests" ,
28+ [" method" , " endpoint" , " status_code" ],
2129)
2230
2331http_request_duration_seconds = Histogram (
24- 'http_request_duration_seconds' ,
25- 'HTTP request duration in seconds' ,
26- ['method' , 'endpoint' ],
27- buckets = (0.01 , 0.025 , 0.05 , 0.075 , 0.1 , 0.25 , 0.5 , 0.75 , 1.0 , 2.5 , 5.0 , 7.5 , 10.0 )
32+ "http_request_duration_seconds" ,
33+ "HTTP request duration in seconds" ,
34+ ["method" , "endpoint" ],
35+ buckets = (
36+ 0.01 ,
37+ 0.025 ,
38+ 0.05 ,
39+ 0.075 ,
40+ 0.1 ,
41+ 0.25 ,
42+ 0.5 ,
43+ 0.75 ,
44+ 1.0 ,
45+ 2.5 ,
46+ 5.0 ,
47+ 7.5 ,
48+ 10.0 ,
49+ ),
2850)
2951
3052http_requests_in_progress = Gauge (
31- ' http_requests_in_progress' ,
32- ' HTTP requests currently being processed' ,
33- [' method' , ' endpoint' ]
53+ " http_requests_in_progress" ,
54+ " HTTP requests currently being processed" ,
55+ [" method" , " endpoint" ],
3456)
3557
3658# Application Metrics
3759active_users = Gauge (
38- ' snippetly_active_users' ,
39- ' Number of active users (logged in last 24h)'
60+ " snippetly_active_users" ,
61+ " Number of active users (logged in last 24h)" ,
4062)
4163
4264total_snippets = Gauge (
43- ' snippetly_total_snippets' ,
44- ' Total number of code snippets'
65+ " snippetly_total_snippets" ,
66+ " Total number of code snippets" ,
4567)
4668
4769database_connections = Gauge (
48- ' snippetly_database_connections' ,
49- ' Number of active database connections' ,
50- [' database' ]
70+ " snippetly_database_connections" ,
71+ " Number of active database connections" ,
72+ [" database" ],
5173)
5274
5375
@@ -61,7 +83,9 @@ class PrometheusMiddleware(BaseHTTPMiddleware):
6183 - Requests in progress gauge
6284 """
6385
64- async def dispatch (self , request : Request , call_next ):
86+ async def dispatch (
87+ self , request : Request , call_next : Callable
88+ ) -> Response :
6589 # Skip metrics endpoint itself to avoid recursion
6690 if request .url .path == "/api/metrics" :
6791 return await call_next (request )
@@ -71,7 +95,9 @@ async def dispatch(self, request: Request, call_next):
7195 method = request .method
7296
7397 # Track request in progress
74- http_requests_in_progress .labels (method = method , endpoint = endpoint ).inc ()
98+ http_requests_in_progress .labels (
99+ method = method , endpoint = endpoint
100+ ).inc ()
75101
76102 # Start timer
77103 start_time = time ()
@@ -85,32 +111,29 @@ async def dispatch(self, request: Request, call_next):
85111 status_code = response .status_code
86112
87113 http_requests_total .labels (
88- method = method ,
89- endpoint = endpoint ,
90- status_code = status_code
114+ method = method , endpoint = endpoint , status_code = status_code
91115 ).inc ()
92116
93117 http_request_duration_seconds .labels (
94- method = method ,
95- endpoint = endpoint
118+ method = method , endpoint = endpoint
96119 ).observe (duration )
97120
98121 return response
99122
100123 except Exception as e :
101124 # Record error
102125 http_requests_total .labels (
103- method = method ,
104- endpoint = endpoint ,
105- status_code = 500
126+ method = method , endpoint = endpoint , status_code = 500
106127 ).inc ()
107128
108129 logger .error (f"Request failed: { method } { endpoint } - { str (e )} " )
109130 raise
110131
111132 finally :
112133 # Decrement in-progress counter
113- http_requests_in_progress .labels (method = method , endpoint = endpoint ).dec ()
134+ http_requests_in_progress .labels (
135+ method = method , endpoint = endpoint
136+ ).dec ()
114137
115138 def _get_endpoint_pattern (self , request : Request ) -> str :
116139 """
@@ -124,9 +147,9 @@ def _get_endpoint_pattern(self, request: Request) -> str:
124147
125148 # Common patterns
126149 patterns = [
127- (' /api/v1/snippets/' , ' /api/v1/snippets/{id}' ),
128- (' /api/v1/users/' , ' /api/v1/users/{id}' ),
129- (' /api/v1/auth/verify/' , ' /api/v1/auth/verify/{token}' ),
150+ (" /api/v1/snippets/" , " /api/v1/snippets/{id}" ),
151+ (" /api/v1/users/" , " /api/v1/users/{id}" ),
152+ (" /api/v1/auth/verify/" , " /api/v1/auth/verify/{token}" ),
130153 ]
131154
132155 for prefix , pattern in patterns :
@@ -143,6 +166,5 @@ def metrics_endpoint() -> Response:
143166 Returns metrics in Prometheus format for scraping.
144167 """
145168 return Response (
146- content = generate_latest (),
147- media_type = CONTENT_TYPE_LATEST
169+ content = generate_latest (), media_type = CONTENT_TYPE_LATEST
148170 )
0 commit comments