forked from mozilla-services/python-dockerflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.py
More file actions
110 lines (90 loc) · 3.37 KB
/
middleware.py
File metadata and controls
110 lines (90 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from __future__ import annotations
import logging
import time
import urllib
from typing import Any, Dict
from asgiref.typing import (
ASGI3Application,
ASGIReceiveCallable,
ASGISendCallable,
ASGISendEvent,
HTTPScope,
)
from ..logging import MozlogHandler, get_or_generate_request_id, request_id_context
class RequestIdMiddleware:
def __init__(
self,
app: ASGI3Application,
) -> None:
self.app = app
async def __call__(
self, scope: HTTPScope, receive: ASGIReceiveCallable, send: ASGISendCallable
) -> None:
if scope["type"] != "http":
return await self.app(scope, receive, send)
headers = {}
for name, value in scope["headers"]:
header_key = name.decode("latin1").lower()
header_val = value.decode("latin1")
headers[header_key] = header_val
rid = get_or_generate_request_id(
headers,
header_name=getattr(
scope["app"].state, "DOCKERFLOW_REQUEST_ID_HEADER_NAME", None
),
)
request_id_context.set(rid)
await self.app(scope, receive, send)
class MozlogRequestSummaryLogger:
def __init__(
self,
app: ASGI3Application,
logger: logging.Logger | None = None,
) -> None:
self.app = app
if logger is None:
logger = logging.getLogger("request.summary")
logger.setLevel(logging.INFO)
handler = MozlogHandler()
handler.setLevel(logging.INFO)
logger.addHandler(handler)
self.logger = logger
async def __call__(
self, scope: HTTPScope, receive: ASGIReceiveCallable, send: ASGISendCallable
) -> None:
if scope["type"] != "http":
return await self.app(scope, receive, send)
info = dict(request_headers={}, response={})
async def inner_send(message: ASGISendEvent) -> None:
if message["type"] == "http.response.start":
info["response"] = message
await send(message)
try:
info["start_time"] = time.time()
await self.app(scope, receive, inner_send)
except Exception as exc:
info["response"]["status"] = 500
raise exc
finally:
info["end_time"] = time.time()
self._log(scope, info)
def _log(self, scope: HTTPScope, info) -> None:
self.logger.info("", extra=self._format(scope, info))
def _format(self, scope: HTTPScope, info) -> Dict[str, Any]:
for name, value in scope["headers"]:
header_key = name.decode("latin1").lower()
header_val = value.decode("latin1")
info["request_headers"][header_key] = header_val
request_duration_ms = (info["end_time"] - info["start_time"]) * 1000.0
fields = {
"agent": info["request_headers"].get("user-agent", ""),
"path": scope["path"],
"method": scope["method"],
"code": info["response"]["status"],
"lang": info["request_headers"].get("accept-language"),
"t": int(request_duration_ms),
"rid": request_id_context.get(),
}
if getattr(scope["app"].state, "DOCKERFLOW_SUMMARY_LOG_QUERYSTRING", False):
fields["querystring"] = urllib.parse.unquote(scope["query_string"].decode())
return fields