Repository navigation
chore(appsec): keep Datadog block-path tags when OTel semantics are off - #20690
Hanane-Laassilia wants to merge 9 commits into
Conversation
Circular import analysis
|
Codeowners resolved asResolved from the full PR diff against |
Dependency direction analysis
|
❌ ErrorsYour PR has failed checks. Please review the issues below and take necessary action before merging. 🚦 23 Pipeline jobs failed
ℹ️ InfoNo other issues found (see more)❄️ No new flaky tests detected 🔄 Datadog retried 24 tests - 0 passed on retry 🚧 1 test that failed was ignored due to quarantine Useful? React with 👍 / 👎 This comment will be updated automatically if new data arrives.🔗 Commit SHA: b21897f | Docs | View more details | Give us feedback! |
BenchmarksBenchmark execution time: 2026-10-08 16:22:37 Comparing candidate commit b21897f in PR branch Found 0 performance improvements and 4 performance regressions! Performance is the same for 441 metrics, 9 unstable metrics, 5 known flaky benchmarks, 5 flaky benchmarks without significant changes.
|
ddfc698 to
e93e5b2
Compare
41d3958 to
8228fd8
Compare
8228fd8 to
3c280da
Compare
3c280da to
1e27692
Compare
1e27692 to
a7874e5
Compare
a7874e5 to
21f9961
Compare
fc38159 to
3dae795
Compare
cfb8806 to
9e2d313
Compare
9e2d313 to
0ed24f8
Compare
0ed24f8 to
03cfcaa
Compare
…s OTel semantics are enabled
…pty Flask query strings
… unit tests they cover
…Sec and aiohttp tests
…tics through the test agent
03cfcaa to
b21897f
Compare
## Description Adds the shared foundation for emitting OpenTelemetry HTTP semantic conventions when `DD_TRACE_OTEL_SEMANTICS_ENABLED=true`. This PR does **not** change HTTP integrations yet. The client and server integrations start using this foundation in #20009 and #20010. This is the first part of the implementation for #19501. ### Configuration `DD_TRACE_OTEL_SEMANTICS_ENABLED` remains disabled by default. When enabled, it enforces the settings required by the OTel HTTP semantics path: - OTLP trace export - Span attribute schema `v0` - Peer-service defaults disabled - Native span events enabled for OTLP export Conflicting settings are overridden and reported through logs and telemetry. ### Shared HTTP semantics The implementation lives under `ddtrace/_trace/otel/http/`: - `tags.py` handles HTTP method normalization, URL attributes, server address and port extraction, query-string handling, status codes, and client/server error classification. - `resource.py` generates low-cardinality HTTP resource names and prevents later instrumentation updates from overwriting resources set by user code. `OTelHTTPSpanAttributes` is shared by the upcoming client and server layers. Those layers will instantiate it from `set_http_meta()` and provide metadata as it becomes available during the request lifecycle. For example: ```python attributes = OTelHTTPSpanAttributes(span, integration_config) attributes.set_method("get") attributes.set_url("https://example.com/users/42") attributes.set_status_code(200) attributes.set_resource("/users/{id}") ``` This produces: ```text http.request.method = "GET" http.request.method_original = "get" url.scheme = "https" url.path = "/users/42" http.response.status_code = 200 resource = "GET /users/{id}" ``` Recreating the helper during later metadata phases is supported: method, resource, and status-error ownership are stored on the span. ### OTel requirements covered The behavior follows the [OpenTelemetry HTTP span conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/). | Scenario | OTel behavior | Covered behavior | | --- | --- | --- | | Method `get` | Normalize the method and preserve its original spelling | Emits `http.request.method="GET"` and `http.request.method_original="get"` | | Unknown method `PROPFIND` | Emit `_OTHER` and retain the original method | Emits `_OTHER`, preserves `PROPFIND`, and uses `HTTP` for resource naming | | Server route `/users/{id}` | Use the low-cardinality route template instead of the raw request path | Refines the resource to `GET /users/{id}` | | Client URL containing credentials | Redact user information from `url.full` | Emits `https://REDACTED:REDACTED@example.com/search` | | Client 4xx response | Mark the span as an error | Sets `span.error=1` and a string status-derived `error.type` | | Server 4xx response | Do not mark the span as an error by default | Leaves `span.error=0` | | Server 5xx response | Mark the span as an error | Sets `span.error=1` | Existing Datadog query capture, query obfuscation, and custom server error-range settings remain supported. Exception information takes precedence over status-derived error metadata. ### Resource ownership Resource naming distinguishes instrumentation-generated values from user replacements: 1. Instrumentation may progressively refine a resource as more metadata becomes available. 2. If user code replaces that resource, subsequent instrumentation updates preserve the user value. 3. Raw request paths are never used as OTel server resources; route templates are used when available. WebSocket resources are left unchanged to avoid collapsing them to the unknown-method resource name. ## Testing Unit coverage includes: - Method normalization and stale `method_original` cleanup - Client and server URL attributes - Credential redaction and query-string obfuscation - Malformed URLs - Client/server status-code error classification - Repeated metadata updates - Exception preservation - User-owned resource preservation - Schema and peer-service overrides - OTLP writer selection and native span events End-to-end OTLP snapshots are added later in the stack because they depend on the harness introduced in #20688: - #20689: HTTP client snapshots - #20690: HTTP server and AppSec snapshots ## Risks The feature remains opt-in. This PR configures the enabled mode and adds the shared implementation, but it does not change emitted HTTP integration spans until the client and server follow-up PRs are merged. ## PR stack 1. #20008 — shared configuration and HTTP semantics foundation 2. #20688 — OTLP snapshot harness 3. #20009 — HTTP client integration 4. #20689 — client configuration and snapshots 5. #20010 — HTTP server/framework and AppSec integration 6. #20690 — AppSec block-path attributes and server snapshots Co-authored-by: Hanane-Laassilia <hanane.laassilia@datadoghq.com>
Description
The Flask (two paths) and FastAPI AppSec block paths were changed to call
set_http_meta(..., request_headers=...)unconditionally. With OTel semantics off that adds tags the previous code never wrote (http.urlwithout a query string, resolved client IP, referrer and header-tracing tags) and dispatches the AppSec and AI Guard listeners a second time, which broke three Flask AppSec snapshots (test_flask_ipblock_match_403,test_flask_ipblock_match_403_json,test_flask_userblock_match_403_json).These paths now use
http_block_metadataandset_url_tags_server, the helpers the Django block path already uses. Both check the semantics flag themselves, so output with the flag off matchesmain, and with the flag on the OTel keys are written.Also on this branch: the aiohttp per-app
trace_query_stringoverride is honored when taggingurl.querywith OTel semantics (b5a0bab8bd).Also adds the server OTLP snapshots:
tests/integration/test_otel_http_server_snapshots.pybuilds server spans in addtrace-runsubprocess and fills them throughOTelHTTPSpanAttributes, without a web framework. Cases: route-based name with raw path, obfuscated query, user agent and both client addresses; unmatched route; unknown method; 302/404/500;DD_TRACE_HTTP_SERVER_ERROR_STATUSES=404-412; query-string tagging disabled (with and without a URL); and an exception's type winning inerror.type. This completes Emmett's request on #20008 to turn theOTelHTTPSpanAttributestests into snapshots, so four unit tests the snapshots now cover are removed fromtests/tracer/test_resource.py.Tracking: APMAPI-2036.
Testing
tests/appsec/appsec/test_block_span_tags.pycalls all three block functions with the flag off and on, and asserts the Datadog or OTel tags and that client IP, referrer andnetwork.client.ipare never set by these paths.tests/integration/test_otel_http_server_snapshots.pyand the client snapshots from chore(tracing): add HTTP client error statuses and OTel client snapshots #20689: 15 passed. The snapshots were reviewed against the RFC (server errors from 500, client errors from 400, integer status code and port,HTTPfor_OTHER, method-only name without a route, nourl.querywhen tagging is off).scripts/lint suitespec-check, style and typing passed.Risks
Only block-path span tags change. If reading the request fails, the span keeps the status only, the same as the Django block path.
Additional Notes
Stacked directly on
maximo/otel-http-server-semantics. The OTLP-shaped snapshots cannot be produced by the test agent's own snapshot endpoint, which is why the harness exists.