chore(flask): emit MicroVM request-starting event - #19816
litianningdatadog wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new internal web-framework event (WebFrameworkEvents.WEB_REQUEST_STARTING) and emits it from the Flask integration at WSGI entry, intended to let downstream logic observe method/path before Flask request context and the root web span are created (motivated by the MicroVM /run identity-refresh ordering constraint).
Changes:
- Add
WebFrameworkEvents.WEB_REQUEST_STARTING = "web.request.starting". - Emit the event from
ddtrace.contrib.internal.flask.patch.patched_wsgi_app()as soon asREQUEST_METHODand path info are available in the WSGI environ. - Add Flask tests asserting the event is emitted (including for 404s) and that emission happens before the Flask WSGI middleware runs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
ddtrace/contrib/_events/web_framework.py |
Adds the new WEB_REQUEST_STARTING enum value. |
ddtrace/contrib/internal/flask/patch.py |
Dispatches the new pre-request event at Flask WSGI entry. |
tests/contrib/flask/test_microvm_identity_refresh.py |
Adds coverage validating the event is emitted and ordered before tracing/middleware. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| events = [] | ||
| environ = {"REQUEST_METHOD": "POST", "PATH_INFO": REQUEST_STARTING_PATH, "SCRIPT_NAME": ""} | ||
|
|
||
| def start_response(status, headers, exc_info=None): | ||
| pass | ||
|
|
||
| def wrapped(environ, start_response): | ||
| return [] | ||
|
|
||
| def dispatch(name, args): | ||
| if name == WebFrameworkEvents.WEB_REQUEST_STARTING.value: | ||
| events.append("starting") | ||
|
|
🎉 All green!🧪 All tests passed 🔗 Commit SHA: a0e3c42 | Docs | View more details | Give us feedback! |
Dependency direction analysis
|
BenchmarksBenchmark execution time: 2026-08-25 00:18:27 Comparing candidate commit a0e3c42 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 70 metrics, 0 unstable metrics.
|
d4c1810 to
12b6c50
Compare
0fa6836 to
cde3045
Compare
Define web request event names in ddtrace.internal.core.event_names so integrations and lower-level runtime hooks can depend on the same event strings without duplicating literals. The MicroVM runtime hook needs WEB_REQUEST_STARTING during ddtrace bootstrap. Importing ddtrace.contrib from that path can load trace handlers that import top-level ddtrace.config before __init__ has exported it, producing a circular import in MicroVM images. Keeping the names in a core constants-only module gives both contrib and runtime a safe dependency point.
cde3045 to
a0e3c42
Compare
| script_name = (environ.get("SCRIPT_NAME") or "").rstrip("/") | ||
| if in_aws_lambda_microvm(): | ||
| path_info = environ.get("PATH_INFO") or "" | ||
| core.dispatch( |
There was a problem hiding this comment.
If we do this from the base wsgi middleware, will it happen early enough for you, and also hit a larger number of web integrations in 1 go?
same with the asgi base setup?
it shouldn't stop us from adding test cases per-integration that this is being handled properly, but would save us from needing to mimic the same event firing from all integrations.
wdyt?
There was a problem hiding this comment.
or, does the existing wsgi.__call__ event happen early enough, we can just hook into that one without needing to add a new event (if there are no listeners, dispatching events is cheap, but still incurs a cost)
There was a problem hiding this comment.
Thanks for the suggestion! Not aware that dd-trace-py already has wsgi/asgi handling. I think patching them would cover many cases. For other cases such as python stdlib http.server, it would require integration work #19817.
| environ, start_response = args | ||
| script_name = (environ.get("SCRIPT_NAME") or "").rstrip("/") | ||
| if in_aws_lambda_microvm(): | ||
| path_info = environ.get("PATH_INFO") or "" |
There was a problem hiding this comment.
I'm not sure I understand entirely what's happening here, so apologies if the question is silly, but shouldn't we be checking for the expected /run request at some point? It looks like we always emit the starting event as soon as we get a request within a MicroVM instance
There was a problem hiding this comment.
yes we can add pre-check here to minimize the event firing. To answer your question, the path check occurs on the listener side in the followup PR
https://github.com/DataDog/dd-trace-py/pull/19825/changes#diff-433e6ca1c168b17797cede9c3332a99224d530e0edbfdd2cceafeafd8bf79c51R105
emmettbutler
left a comment
There was a problem hiding this comment.
Marked as draft because the base is not main.
|
close it as it is covered by wsgi/asgi patch |
Stacked PRs:
Description
Add a Flask request-starting event that fires before the request/root span is created, but only when running in an AWS Lambda MicroVM environment.
The immediate use case is AWS Lambda MicroVMs: the
POST /aws/lambda-microvms/runtime/v1/runhook needs to refresh runtime identity before the root span reads it. Flask receives mounted/prefixed WSGI requests asSCRIPT_NAMEplusPATH_INFO, so the dispatched path uses the canonical WSGI request path:(SCRIPT_NAME or "").rstrip("/") + (PATH_INFO or "").This PR also adds
ddtrace.internal.serverless.in_aws_lambda_microvm()so future web integrations can share the same MicroVM environment check instead of each integration readingAWS_LAMBDA_MICROVM_IMAGE_ARNdirectly.I also moved the web event-name strings into
ddtrace.internal.core.event_names. That gives lower-level code a shared place to importWEB_REQUEST_STARTINGwithout depending onddtrace.contrib. Importing contrib duringimport ddtraceis unsafe because it can pull in trace handlers beforeddtrace.configis exported.Replaces #19779 after the branch rename broke the original PR association.
Testing
scripts/lint fmt ddtrace/internal/serverless/__init__.py ddtrace/contrib/internal/flask/patch.py tests/contrib/flask/test_microvm_identity_refresh.py tests/internal/test_serverless.pyuv run --script scripts/import-analysis/cycles.py analyze /tmp/ddtrace-cycles-after.jsonddtrace.internal.serverless,ddtrace.contrib.internal.flask.patch, orddtrace.internal.settings; existing repo total remains 3.scripts/run-tests --venv 12c5734 -- -- tests/internal/test_serverless.pyImportError: cannot import name 'process_metrics' from 'ddtrace.internal.native._native'scripts/run-tests --venv f3bee4b -- -- tests/contrib/flask/test_microvm_identity_refresh.pyRisks
Low. The Flask dispatch is internal and gated to AWS Lambda MicroVM environments.
Release note
None. Internal-only event plumbing; use
changelog/no-changelog.Stack
Depends on #19778. Follow-ups add more emitters and the runtime-id refresh listener.