Skip to content

feat(httpx2): add httpx2 instrumentation - #19792

Open
dubloom wants to merge 3 commits into
mainfrom
dubloom/httpx2
Open

feat(httpx2): add httpx2 instrumentation#19792
dubloom wants to merge 3 commits into
mainfrom
dubloom/httpx2

Conversation

@dubloom

@dubloom dubloom commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Fulfill #18780

Description

Add httpx2 integration. httpx2 is a fork of httpx (which is not unmaintained) so the implementation is almost if not completely the same as httpx integration.

It seems httpx2 is compatible with websocket so it might be worth supporting that in a follow up

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 20, 2026

Copy link
Copy Markdown

Circular import analysis

⚠️ Existing circular imports

There are 3 circular imports that already exist on the base branch and have not been changed by this PR.

ddtrace.errortracking._handled_exceptions.bytecode_injector -> ddtrace.errortracking._handled_exceptions.callbacks -> ddtrace.errortracking._handled_exceptions.collector -> ddtrace.errortracking._handled_exceptions.bytecode_reporting -> ddtrace.errortracking._handled_exceptions.bytecode_injector
ddtrace.llmobs -> ddtrace.llmobs._evaluators -> ddtrace.llmobs._evaluators.format -> ddtrace.llmobs._experiment -> ddtrace.llmobs
ddtrace.appsec._asm_request_context -> ddtrace.appsec._iast._iast_request_context_base -> ddtrace.appsec._iast._iast_env -> ddtrace.appsec._iast.reporter -> ddtrace.appsec._exploit_prevention.stack_traces -> ddtrace.appsec._asm_request_context

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 20, 2026

Copy link
Copy Markdown

Dependency direction analysis

⚠️ Existing dependency direction violations

There are 252 dependency direction violations that already exist on the base branch and have not been changed by this PR.

Show existing violations (showing 5 of 252 highest severity)
ddtrace.internal.tracemethods -×-> ddtrace.trace  (internal-core -> product:tracing, score=135)
ddtrace.llmobs._integrations.crewai -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)
ddtrace.appsec._listeners -×-> ddtrace.trace  (product:appsec -> product:tracing, score=133)
ddtrace.llmobs._integrations.claude_agent_sdk -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)
ddtrace.llmobs._integrations.litellm -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)

To see all violations, download the layers-base.json and layers-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/layers.py compare layers-base.json layers-pr.json

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codeowners resolved as

Resolved from the full PR diff against main using the target branch CODEOWNERS file.
CODEOWNERS team requests not listed below are not required by the current file set.

.riot/requirements/13c3933.txt                                          @DataDog/apm-python
.riot/requirements/143e698.txt                                          @DataDog/apm-python
.riot/requirements/1e7c279.txt                                          @DataDog/apm-python
.riot/requirements/2a0ccf2.txt                                          @DataDog/apm-python
.riot/requirements/a77aa22.txt                                          @DataDog/apm-python
.riot/requirements/da479b1.txt                                          @DataDog/apm-python
.riot/requirements/e49b70d.txt                                          @DataDog/apm-python
.riot/requirements/e9af4c9.txt                                          @DataDog/apm-python
.riot/requirements/f2c3ec9.txt                                          @DataDog/apm-python
.riot/requirements/f38b0e1.txt                                          @DataDog/apm-python
.riot/requirements/fd33cd8.txt                                          @DataDog/apm-python
ddtrace/_monkey.py                                                      @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/contrib/internal/httpx/patch.py                                 @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/contrib/internal/httpx/utils.py                                 @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/contrib/internal/httpx2/__init__.py                             @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/contrib/internal/httpx2/patch.py                                @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/internal/settings/_config.py                                    @DataDog/python-guild @DataDog/apm-sdk-capabilities-python
ddtrace/internal/settings/_supported_configurations.py                  @DataDog/apm-python
docs/index.rst                                                          @DataDog/python-guild
docs/integrations.rst                                                   @DataDog/python-guild
releasenotes/notes/add-httpx2-integration-b72bee56ccc4e55c.yaml         @DataDog/apm-python
riotfile.py                                                             @DataDog/apm-python
scripts/integration_registry/registry.yaml                              @DataDog/apm-idm-python
supported-configurations.json                                           @DataDog/apm-python
supported_versions.json                                                 @DataDog/apm-core-python @DataDog/apm-idm-python
tests/contrib/httpx2/__init__.py                                        @DataDog/apm-core-python @DataDog/apm-idm-python
tests/contrib/httpx2/test_httpx2.py                                     @DataDog/apm-core-python @DataDog/apm-idm-python
tests/contrib/httpx2/test_httpx2_patch.py                               @DataDog/apm-core-python @DataDog/apm-idm-python
tests/contrib/suitespec.yml                                             @DataDog/python-guild
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_alias_httpx.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_configure_service_name.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_configure_service_name_env.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_connection_error.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_get_200.json      @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_get_500.json      @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_httpx2_service_name.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_request_headers.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_configure_global_service_name_env_default.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_configure_global_service_name_env_v0.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_configure_global_service_name_env_v1.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_operation_name_env_v0.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_operation_name_env_v1.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_unspecified_service_name_env_default.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_unspecified_service_name_env_v0.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_schematized_unspecified_service_name_env_v1.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_split_by_domain.json  @DataDog/apm-python
tests/snapshots/tests.contrib.httpx2.test_httpx2.test_trace_query_string.json  @DataDog/apm-python

@datadog-official

datadog-official Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🔄 Datadog auto-retried 3 jobs - 3 passed on retry View in Datadog

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 5e06c02 | Docs | View more details | Give us feedback!

@pr-commenter

pr-commenter Bot commented Aug 20, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-08-20 15:53:22

Comparing candidate commit 5e06c02 in PR branch dubloom/httpx2 with baseline commit 1530b7b in branch main.

📊 Benchmarking dashboard

Found 0 performance improvements and 9 performance regressions! Performance is the same for 611 metrics, 10 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:httppropagationextract-empty_headers

  • 🟥 execution_time [+202.237ns; +232.553ns] or [+20.796%; +23.914%]

scenario:httppropagationinject-ids_only

  • 🟥 execution_time [+1.676µs; +1.791µs] or [+8.743%; +9.342%]

scenario:iastaspects-rstrip_aspect

  • 🟥 execution_time [+43.631µs; +56.495µs] or [+11.169%; +14.461%]

scenario:iastaspects-stringio_noaspect

  • 🟥 execution_time [+24.056µs; +28.677µs] or [+7.275%; +8.672%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+112.765µs; +117.185µs] or [+27.416%; +28.491%]

scenario:iastaspectssplit-rsplit_aspect

  • 🟥 execution_time [+12.037µs; +18.585µs] or [+8.073%; +12.465%]

scenario:span-start

  • 🟥 execution_time [+1.125ms; +1.284ms] or [+7.407%; +8.455%]

scenario:telemetryaddmetric-1-count-metric-1-times

  • 🟥 execution_time [+415.183ns; +453.549ns] or [+15.839%; +17.303%]

scenario:tracer-small

  • 🟥 execution_time [+27.508µs; +30.204µs] or [+8.139%; +8.937%]

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:coreapiscenario-context_with_data_listeners

  • unstable execution_time [-888.804ns; +596.540ns] or [-8.028%; +5.388%]

scenario:coreapiscenario-core_dispatch_1_listener

  • unstable execution_time [-36.259ns; +30.346ns] or [-5.885%; +4.925%]

scenario:coreapiscenario-core_dispatch_50_listeners

  • unstable execution_time [-1721.869ns; +1562.763ns] or [-10.107%; +9.174%]

scenario:coreapiscenario-core_dispatch_exception_listeners

  • unstable execution_time [-1167.528ns; +1370.621ns] or [-8.868%; +10.410%]

scenario:coreapiscenario-core_dispatch_listeners

  • unstable execution_time [-345.961ns; +305.723ns] or [-9.407%; +8.313%]

scenario:coreapiscenario-core_dispatch_no_args_listeners

  • unstable execution_time [-278.031ns; +228.388ns] or [-9.529%; +7.828%]

scenario:coreapiscenario-core_dispatch_with_results_1_listener

  • unstable execution_time [-83.380ns; +58.254ns] or [-7.296%; +5.097%]

scenario:coreapiscenario-core_dispatch_with_results_50_listeners

  • unstable execution_time [-3961.725ns; +3947.185ns] or [-9.778%; +9.742%]

scenario:coreapiscenario-core_dispatch_with_results_listeners

  • unstable execution_time [-929.880ns; +620.581ns] or [-11.391%; +7.602%]

scenario:packagesupdateimporteddependencies-import_many_stdlib_cached

  • unstable execution_time [-66.391µs; +55.084µs] or [-10.255%; +8.508%]

@dubloom
dubloom marked this pull request as ready for review August 20, 2026 15:21
@dubloom
dubloom requested review from a team as code owners August 20, 2026 15:21

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e06c0235b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ddtrace/_monkey.py
"gevent": True,
"graphql": True,
"grpc": True,
"httpx2": True,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the httpx2 disable flag for aliased imports

When an application calls httpx2.alias_httpx() and sets DD_TRACE_HTTPX2_ENABLED=false (or calls patch_all(httpx2=False)), this entry is disabled but the existing httpx hook remains active. Because httpx then refers to the same aliased module, the HTTPX patch still wraps its clients and emits spans using config.httpx, so users cannot disable HTTPX2 instrumentation without also disabling the separate HTTPX integration. Coordinate the hooks or detect the aliased module so the HTTPX2 enablement setting remains authoritative.

AGENTS.md reference: AGENTS.md:L59-L62

Useful? React with 👍 / 👎.

@dubloom dubloom Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Man this is self sabotaging if someone is doing that

Comment on lines +49 to +65
with core.context_with_event(
event=HttpClientSendEvent(
request_url=httpx_url_to_str(request.url),
request_method=request.method,
request_headers=request.headers,
request_body=lambda: request.content,
),
context_name_override=HttpClientEvents.HTTPX_SEND_REQUEST.value,
) as ctx:
response = None
try:
response = wrapped(*args, **kwargs)
return response
finally:
if response is not None:
ctx.event.set_response(response)
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only comment on the pr is that these are literally line by line the same as the current patch methods in httpx, wondering if we should de dedupe.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, it would make sense but if httpx2 start to deviate from httpx it could complexify the work so I think I'd prefer to duplicate but this is a personal preference and I don't have super strong opinions

Comment on lines +103 to +121
with core.context_with_event(
HttpClientRequestEvent(
http_operation="http.request",
service=httpx_get_service_name(request, config.httpx2),
component=config.httpx2.integration_name,
request_method=request.method,
request_headers=request.headers,
integration_config=config.httpx2,
request_url=httpx_url_to_str(request.url),
query=ensure_text(request.url.query),
target_host=request.url.host,
),
context_name_override=HttpClientEvents.HTTPX_REQUEST.value,
) as ctx:
response = None
try:
response = await wrapped(*args, **kwargs)
return response
finally:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for all the patch functions, tho this would need config passed in

@wconti27 wconti27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 nit around patches being duped code from httpx to httpx2, may want to consider merging those code paths

query=ensure_text(request.url.query),
target_host=request.url.host,
),
context_name_override=HttpClientEvents.HTTPX_REQUEST.value,

@florentinl florentinl Aug 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to use a dedicated HTTPX2_REQUEST event name initially? (same for HTTPX2_SEND_REQUEST)

This would send events to the AppSec subscriber even though we don’t currently have test coverage confirming that httpx2 works with AppSec.

Since this is a fork of httpx, I expect it to work and to be semantically correct, but still I’d prefer to keep the two separate until that has been properly tested.

Would be happy to unify both again once I have added the necessary test coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants