|
5 | 5 |
|
6 | 6 | from aiohttp.web import Application, Request, Response, middleware |
7 | 7 | from aiohttp.web_exceptions import HTTPException, HTTPInternalServerError |
8 | | -from multidict import CIMultiDictProxy # noqa: TCH002 |
9 | 8 | from opentelemetry import trace |
10 | | -from opentelemetry.instrumentation.asgi import collect_request_attributes |
11 | 9 | from opentelemetry.instrumentation.utils import http_status_to_status_code |
12 | 10 | from opentelemetry.metrics import Meter, MeterProvider, get_meter_provider |
13 | 11 | from opentelemetry.propagate import extract |
|
32 | 30 | _OTEL_SCHEMA = "https://opentelemetry.io/schemas/1.11.0" |
33 | 31 |
|
34 | 32 |
|
35 | | -_DURATION_ATTRS = [ |
36 | | - SpanAttributes.HTTP_METHOD, |
37 | | - SpanAttributes.HTTP_HOST, |
38 | | - SpanAttributes.HTTP_SCHEME, |
39 | | - SpanAttributes.HTTP_STATUS_CODE, |
40 | | - SpanAttributes.HTTP_FLAVOR, |
41 | | - SpanAttributes.HTTP_SERVER_NAME, |
42 | | - SpanAttributes.NET_HOST_NAME, |
43 | | - SpanAttributes.NET_HOST_PORT, |
44 | | - SpanAttributes.HTTP_ROUTE, |
45 | | -] |
46 | | - |
47 | | -_ACTIVE_REQUESTS_COUNT_ATTRS = [ |
48 | | - SpanAttributes.HTTP_METHOD, |
49 | | - SpanAttributes.HTTP_HOST, |
50 | | - SpanAttributes.HTTP_SCHEME, |
51 | | - SpanAttributes.HTTP_FLAVOR, |
52 | | - SpanAttributes.HTTP_SERVER_NAME, |
53 | | -] |
54 | | - |
55 | | - |
56 | 33 | class AiohttpGetter(Getter): |
57 | | - def get(self, carrier: dict, key: str) -> list | None: |
58 | | - headers: CIMultiDictProxy = carrier.headers # type: ignore[attr-defined] |
59 | | - if not headers: |
60 | | - return None |
61 | | - return headers.getall(key, None) |
| 34 | + def get(self, carrier: Request, key: str) -> list[str] | None: |
| 35 | + headers = carrier.headers |
| 36 | + return headers.getall(key, None) if headers else None |
62 | 37 |
|
63 | | - def keys(self, carrier: dict) -> list: |
| 38 | + def keys(self, carrier: Request) -> list[str]: |
64 | 39 | return list(carrier.keys()) |
65 | 40 |
|
66 | 41 |
|
67 | | -def _get_tracer(tracer_provider: TracerProvider | None = None) -> Tracer: |
68 | | - return trace.get_tracer( |
69 | | - __name__, |
70 | | - tracer_provider=tracer_provider, |
71 | | - schema_url=_OTEL_SCHEMA, |
72 | | - ) |
73 | | - |
74 | | - |
75 | | -def _get_meter( |
76 | | - name: str, |
77 | | - meter_provider: MeterProvider | None = None, |
78 | | - schema_url: str | None = None, |
79 | | -) -> Meter: |
80 | | - if meter_provider is None: |
81 | | - meter_provider = get_meter_provider() |
82 | | - return meter_provider.get_meter(name=name, schema_url=schema_url) |
83 | | - |
84 | | - |
85 | | -def _parse_duration_attrs(req_attrs: dict[str, Any]) -> dict[str, Any]: |
86 | | - duration_attrs = {} |
87 | | - for attr_key in _DURATION_ATTRS: |
88 | | - if req_attrs.get(attr_key) is not None: |
89 | | - duration_attrs[attr_key] = req_attrs[attr_key] |
90 | | - return duration_attrs |
91 | | - |
92 | | - |
93 | | -def _parse_active_request_count_attrs(req_attrs: dict[str, Any]) -> dict[str, Any]: |
94 | | - active_requests_count_attrs = {} |
95 | | - for attr_key in _ACTIVE_REQUESTS_COUNT_ATTRS: |
96 | | - if req_attrs.get(attr_key) is not None: |
97 | | - active_requests_count_attrs[attr_key] = req_attrs[attr_key] |
98 | | - return active_requests_count_attrs |
99 | | - |
100 | | - |
101 | | -def _get_default_span_details(request: Request) -> tuple[str, dict]: |
102 | | - span_name = request.path.strip() or f"HTTP {request.method}" |
103 | | - return span_name, {} |
| 42 | +def _get_default_span_details(request: Request) -> tuple[str, dict[str, Any]]: |
| 43 | + span_attributes: dict[str, Any] = { |
| 44 | + SpanAttributes.HTTP_SCHEME: request.scheme, |
| 45 | + SpanAttributes.HTTP_HOST: request.host, |
| 46 | + SpanAttributes.NET_HOST_PORT: request.url.port, |
| 47 | + SpanAttributes.HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}", |
| 48 | + SpanAttributes.HTTP_TARGET: request.path_qs, |
| 49 | + SpanAttributes.HTTP_URL: str(request.url), |
| 50 | + SpanAttributes.HTTP_METHOD: request.method, |
| 51 | + SpanAttributes.HTTP_SERVER_NAME: request.headers.get("Host", ""), |
| 52 | + SpanAttributes.HTTP_USER_AGENT: request.headers.get("User-Agent", ""), |
| 53 | + SpanAttributes.NET_PEER_IP: request.remote, |
| 54 | + } |
| 55 | + |
| 56 | + if request.transport: |
| 57 | + span_attributes[SpanAttributes.NET_PEER_PORT] = request.transport.get_extra_info("peername")[1] |
| 58 | + if request.match_info.route and request.match_info.route.resource: |
| 59 | + route = request.match_info.route.resource.canonical |
| 60 | + span_attributes[SpanAttributes.HTTP_ROUTE] = route |
| 61 | + else: |
| 62 | + route = request.path |
| 63 | + span_attributes[SpanAttributes.HTTP_ROUTE] = route |
| 64 | + |
| 65 | + return f"{request.method} {route or 'unknown'}", span_attributes |
104 | 66 |
|
105 | 67 |
|
106 | 68 | def _set_status_code(span: trace.Span, status_code: int) -> None: |
@@ -216,19 +178,21 @@ def build_tracing_middleware(config: TracingConfig) -> Callable[..., Coroutine]: |
216 | 178 |
|
217 | 179 | @middleware |
218 | 180 | async def tracing_middleware(request: Request, handler: Callable) -> Any: |
219 | | - span_name, additional_attributes = config.scope_span_details_extractor(request) |
220 | | - |
221 | | - req_attrs = collect_request_attributes(request) |
222 | | - duration_attrs = _parse_duration_attrs(req_attrs) |
223 | | - active_requests_count_attrs = _parse_active_request_count_attrs(req_attrs) |
| 181 | + span_name, attributes = config.scope_span_details_extractor(request) |
| 182 | + active_requests_count_attrs = { |
| 183 | + SpanAttributes.HTTP_SERVER_NAME: attributes[SpanAttributes.HTTP_SERVER_NAME], |
| 184 | + SpanAttributes.HTTP_SCHEME: attributes[SpanAttributes.HTTP_SCHEME], |
| 185 | + SpanAttributes.HTTP_HOST: attributes[SpanAttributes.HTTP_HOST], |
| 186 | + SpanAttributes.HTTP_FLAVOR: attributes[SpanAttributes.HTTP_FLAVOR], |
| 187 | + SpanAttributes.HTTP_METHOD: attributes[SpanAttributes.HTTP_METHOD], |
| 188 | + } |
| 189 | + duration_attrs = {SpanAttributes.HTTP_ROUTE: attributes[SpanAttributes.HTTP_ROUTE]} |
224 | 190 |
|
225 | 191 | with tracer.start_as_current_span( |
226 | 192 | span_name, |
227 | 193 | context=extract(request, getter=getter), |
228 | 194 | kind=trace.SpanKind.SERVER, |
229 | 195 | ) as span: |
230 | | - attributes = collect_request_attributes(request) |
231 | | - attributes.update(additional_attributes) |
232 | 196 | span.set_attributes(attributes) |
233 | 197 | start = default_timer() |
234 | 198 | active_requests_counter.add(1, active_requests_count_attrs) |
@@ -275,3 +239,21 @@ def setup_metrics(app: Application, config: MetricsConfig) -> None: |
275 | 239 |
|
276 | 240 | def setup_tracing(app: Application, config: TracingConfig) -> None: |
277 | 241 | app.middlewares.append(build_tracing_middleware(config)) |
| 242 | + |
| 243 | + |
| 244 | +def _get_tracer(tracer_provider: TracerProvider | None = None) -> Tracer: |
| 245 | + return trace.get_tracer( |
| 246 | + __name__, |
| 247 | + tracer_provider=tracer_provider, |
| 248 | + schema_url=_OTEL_SCHEMA, |
| 249 | + ) |
| 250 | + |
| 251 | + |
| 252 | +def _get_meter( |
| 253 | + name: str, |
| 254 | + meter_provider: MeterProvider | None = None, |
| 255 | + schema_url: str | None = None, |
| 256 | +) -> Meter: |
| 257 | + if meter_provider is None: |
| 258 | + meter_provider = get_meter_provider() |
| 259 | + return meter_provider.get_meter(name=name, schema_url=schema_url) |
0 commit comments