|
8 | 8 | import uuid |
9 | 9 | from collections.abc import Iterable |
10 | 10 | from datetime import UTC, datetime |
11 | | -from typing import Any |
| 11 | +from typing import Any, TypedDict |
12 | 12 |
|
13 | 13 | from flask import g, has_request_context, request |
14 | 14 | from werkzeug.exceptions import HTTPException |
|
43 | 43 | } |
44 | 44 |
|
45 | 45 |
|
| 46 | +class RequestLogPayload(TypedDict, total=False): |
| 47 | + event: str |
| 48 | + route: str |
| 49 | + method: str |
| 50 | + status: int |
| 51 | + duration_ms: float |
| 52 | + request_id: str |
| 53 | + path: str |
| 54 | + source: str |
| 55 | + stale: bool |
| 56 | + error: str |
| 57 | + client_ip: str |
| 58 | + |
| 59 | + |
| 60 | +class ProviderLogPayload(TypedDict, total=False): |
| 61 | + event: str |
| 62 | + provider: str |
| 63 | + base: str |
| 64 | + status: str |
| 65 | + duration_ms: float |
| 66 | + request_id: str | None |
| 67 | + source: str |
| 68 | + stale: bool |
| 69 | + error: str |
| 70 | + |
| 71 | + |
46 | 72 | class JSONLogFormatter(logging.Formatter): |
47 | 73 | """Format LogRecord instances into structured JSON strings.""" |
48 | 74 |
|
@@ -170,25 +196,27 @@ def _build_request_log_extra( |
170 | 196 | request_id: str | None, |
171 | 197 | duration_ms: float | None, |
172 | 198 | error: str | None, |
173 | | -) -> dict[str, Any]: |
| 199 | +) -> RequestLogPayload: |
174 | 200 | route = request.url_rule.rule if request.url_rule else request.path |
175 | | - payload: dict[str, Any] = { |
| 201 | + payload: RequestLogPayload = { |
176 | 202 | "event": event, |
177 | 203 | "route": route, |
178 | 204 | "method": request.method, |
179 | 205 | "status": status, |
180 | | - "duration_ms": round(duration_ms, 3) if duration_ms is not None else None, |
181 | | - "request_id": request_id, |
182 | 206 | "path": request.path, |
183 | 207 | "source": "api", |
184 | 208 | "stale": False, |
185 | 209 | } |
| 210 | + if duration_ms is not None: |
| 211 | + payload["duration_ms"] = round(duration_ms, 3) |
| 212 | + if request_id: |
| 213 | + payload["request_id"] = request_id |
186 | 214 | if error: |
187 | 215 | payload["error"] = error |
188 | 216 | if request.remote_addr: |
189 | 217 | payload["client_ip"] = request.remote_addr |
190 | 218 |
|
191 | | - return {key: value for key, value in payload.items() if value is not None} |
| 219 | + return payload |
192 | 220 |
|
193 | 221 |
|
194 | 222 | def _extract_extras(record_dict: dict[str, Any]) -> dict[str, Any]: |
@@ -243,20 +271,21 @@ def provider_log_extra( |
243 | 271 | duration_ms: float | None, |
244 | 272 | stale: bool, |
245 | 273 | error: str | None = None, |
246 | | -) -> dict[str, Any]: |
247 | | - payload: dict[str, Any] = { |
| 274 | +) -> ProviderLogPayload: |
| 275 | + payload: ProviderLogPayload = { |
248 | 276 | "event": event, |
249 | 277 | "provider": provider, |
250 | 278 | "base": base, |
251 | 279 | "status": status, |
252 | | - "duration_ms": round(duration_ms, 3) if duration_ms is not None else None, |
253 | | - "request_id": _current_request_id(), |
254 | 280 | "source": provider, |
255 | 281 | "stale": stale, |
256 | 282 | } |
| 283 | + if duration_ms is not None: |
| 284 | + payload["duration_ms"] = round(duration_ms, 3) |
| 285 | + payload["request_id"] = _current_request_id() |
257 | 286 | if error: |
258 | 287 | payload["error"] = error |
259 | | - return {key: value for key, value in payload.items() if value is not None} |
| 288 | + return payload |
260 | 289 |
|
261 | 290 |
|
262 | 291 | def _current_request_id() -> str | None: |
|
0 commit comments