1414from slowapi .errors import RateLimitExceeded
1515from prometheus_client import Counter , Histogram , Gauge , generate_latest , CONTENT_TYPE_LATEST
1616import logging
17+ import sys
1718import time
1819import uuid
20+ from pythonjsonlogger import jsonlogger
1921
2022from app .routers import (
2123 health_checks ,
3032from app .middleware .cache import cache_manager
3133from app .services .vault import vault_client
3234
33- # Configure logging
34- logging .basicConfig (
35- level = logging . INFO ,
36- format = " %(asctime)s - %(name)s - %(levelname)s - %(message)s"
35+ # Configure structured JSON logging (matches code-first implementation)
36+ logHandler = logging .StreamHandler ( sys . stdout )
37+ formatter = jsonlogger . JsonFormatter (
38+ ' %(asctime)s %(name)s %(levelname)s %(message)s %(request_id)s %(method)s %(path)s %(status_code)s %(duration_ms)s'
3739)
40+ logHandler .setFormatter (formatter )
3841logger = logging .getLogger (__name__ )
42+ logger .addHandler (logHandler )
43+ logger .setLevel (logging .INFO )
44+
45+ # Disable default basicConfig
46+ logging .getLogger ().handlers .clear ()
47+ logging .getLogger ().addHandler (logHandler )
3948
4049# Prometheus metrics
4150http_requests_total = Counter (
6877# Create FastAPI app
6978app = FastAPI (
7079 title = "DevStack Core - Reference API (API-First)" ,
71- version = "1.0 .0" ,
80+ version = "1.1 .0" ,
7281 description = "API-First implementation generated from OpenAPI specification" ,
7382 docs_url = "/docs" ,
7483 redoc_url = "/redoc" ,
@@ -112,10 +121,10 @@ async def metrics_middleware(request: Request, call_next):
112121 request .state .request_id = request_id
113122
114123 method = request .method
115- path = request .url .path
124+ endpoint = request .url .path
116125
117126 # Track in-progress requests
118- http_requests_in_progress .labels (method = method , endpoint = path ).inc ()
127+ http_requests_in_progress .labels (method = method , endpoint = endpoint ).inc ()
119128
120129 start_time = time .time ()
121130
@@ -126,23 +135,58 @@ async def metrics_middleware(request: Request, call_next):
126135 # Record metrics
127136 http_requests_total .labels (
128137 method = method ,
129- endpoint = path ,
138+ endpoint = endpoint ,
130139 status = response .status_code
131140 ).inc ()
132141
133142 http_request_duration_seconds .labels (
134143 method = method ,
135- endpoint = path
144+ endpoint = endpoint
136145 ).observe (duration )
137146
147+ # Log request with structured data (matches code-first implementation)
148+ logger .info (
149+ "HTTP request completed" ,
150+ extra = {
151+ "request_id" : request_id ,
152+ "method" : method ,
153+ "path" : endpoint ,
154+ "status_code" : response .status_code ,
155+ "duration_ms" : round (duration * 1000 , 2 )
156+ }
157+ )
158+
138159 # Add headers
139160 response .headers ["X-Request-ID" ] = request_id
140161 response .headers ["X-Response-Time" ] = f"{ duration :.3f} s"
141162
142163 return response
143164
165+ except Exception as e :
166+ # Record error metrics
167+ duration = time .time () - start_time
168+ http_requests_total .labels (
169+ method = method ,
170+ endpoint = endpoint ,
171+ status = 500
172+ ).inc ()
173+
174+ # Log error with structured data
175+ logger .error (
176+ f"Request failed: { str (e )} " ,
177+ extra = {
178+ "request_id" : request_id ,
179+ "method" : method ,
180+ "path" : endpoint ,
181+ "status_code" : 500 ,
182+ "duration_ms" : round (duration * 1000 , 2 )
183+ },
184+ exc_info = True
185+ )
186+ raise
187+
144188 finally :
145- http_requests_in_progress .labels (method = method , endpoint = path ).dec ()
189+ http_requests_in_progress .labels (method = method , endpoint = endpoint ).dec ()
146190
147191
148192# Include routers
@@ -158,7 +202,7 @@ async def metrics_middleware(request: Request, call_next):
158202async def startup_event ():
159203 """Application startup event handler."""
160204 # Set app info metric
161- app_info .labels (version = "1.0 .0" , name = "api-first" ).set (1 )
205+ app_info .labels (version = "1.1 .0" , name = "api-first" ).set (1 )
162206
163207 # Initialize response caching with Redis
164208 try :
@@ -171,10 +215,14 @@ async def startup_event():
171215 logger .error (f"Failed to initialize cache: { e } " )
172216 logger .warning ("Application will continue without caching" )
173217
174- logger .info ("Starting API-First FastAPI application..." )
175- logger .info (f"Debug mode: { settings .DEBUG } " )
176- logger .info (f"Vault address: { settings .VAULT_ADDR } " )
177- logger .info (f"Redis cache enabled: { cache_manager .enabled } " )
218+ logger .info (
219+ "Starting DevStack Core Reference API (API-First)" ,
220+ extra = {
221+ "vault_address" : settings .VAULT_ADDR ,
222+ "redis_cache_enabled" : cache_manager .enabled ,
223+ "version" : "1.1.0"
224+ }
225+ )
178226 logger .info ("Application ready" )
179227
180228
@@ -195,7 +243,7 @@ async def root(request: Request):
195243 """
196244 return {
197245 "name" : "DevStack Core Reference API" ,
198- "version" : "1.0 .0" ,
246+ "version" : "1.1 .0" ,
199247 "description" : "Reference implementation for infrastructure integration" ,
200248 "docs" : "/docs" ,
201249 "health" : "/health/all" ,
@@ -253,7 +301,8 @@ async def root(request: Request):
253301
254302
255303@app .get ("/metrics" )
256- async def metrics ():
304+ @limiter .limit ("1000/minute" ) # High limit for metrics scraping
305+ async def metrics (request : Request ):
257306 """Prometheus metrics endpoint"""
258307 return Response (
259308 content = generate_latest (),
0 commit comments