Skip to content

Support host-aware HTTP resource name normalization - #13048

Open
RahulPrasanna-code wants to merge 1 commit into
DataDog:masterfrom
RahulPrasanna-code:feat/issue-6886-host-aware-resource-mapping
Open

RahulPrasanna-code wants to merge 1 commit into
DataDog:masterfrom
RahulPrasanna-code:feat/issue-6886-host-aware-resource-mapping

Conversation

@RahulPrasanna-code

Copy link
Copy Markdown

What Does This Do

Extends the HTTP resource name normalization pipeline to support host-aware pattern matching and placeholder interpolation in custom path resource name mappings (dd.trace.http.client.path-resource-name-mapping and dd.trace.http.server.path-resource-name-mapping):

  1. Host-Prefixed Pattern Matching: Enables patterns like auth.provider-a.com/oauth2/token:host_a_oauth and wildcard hosts like *.provider-b.com/oauth2/*:provider_b_oauth to match against host + path, allowing disambiguation of identical paths across different remote providers.
  2. Exact Path Mappings: Allows exact path mappings (e.g., /oauth2/token:oauth or host-a.com/oauth2/token:host_a_oauth) without requiring wildcard characters (* or ?), which were previously filtered out during normalizer initialization.
  3. {host} Placeholder Interpolation: Supports {host} placeholder interpolation in replacement values (e.g., /oauth2/token:{host}_oauth produces auth.provider-a.com_oauth).
  4. Host Propagation: Updates HttpClientDecorator, HttpResourceDecorator, HttpResourceNames, and UrlInstrumentation to pass host to the normalizer chain while preserving 100% backward compatibility for all existing 3-argument methods and path-only configurations.

Motivation

Solves #6886

When a service makes HTTP client calls to multiple third-party endpoints sharing identical URI paths (such as https://auth.provider-a.com/oauth2/token and https://login.provider-b.com/oauth2/token), the tracer previously stripped the host and normalized both spans to the exact same resource.name (POST /oauth2/token). This prevented distinguishing metrics per external provider in Datadog APM by resource name.

Additional Notes

  • Full Backward Compatibility: The existing 3-argument signatures (withClientPath(span, method, path), setForClient(span, method, path, encoded), etc.) are preserved and delegate cleanly with host = null. Path-only rules continue matching as before.
  • Cache Optimization: Internal cache in AntPatternHttpPathNormalizer maps Pair<String, String> (host, path) to ensure fast normalization without repeated pattern evaluations.
  • Targeted Tests Added:
    • AntPatternHttpPathNormalizerTest: exact path matching, host-prefixed rules, wildcard hosts, schemes normalization, and {host} placeholder interpolation.
    • HttpResourceDecoratorTest: host disambiguation, wildcard host matching, {host} interpolation, and fallback compatibility with null host.
    • HttpClientDecoratorTest: end-to-end span decoration with host-aware client mapping.

Contributor Checklist

  • Format the title according to the contribution guidelines
  • Assign the type: and (comp: or inst:) labels in addition to any other useful labels
  • Avoid using close, fix, or any linking keywords when referencing an issue (used Solves #6886)
  • Update public documentation with new configuration behaviors

- Support host-prefixed mapping patterns in AntPatternHttpPathNormalizer (e.g., auth.example.com/oauth2/token:oauth)
- Support exact URL path mappings without requiring wildcard characters
- Support {host} placeholder interpolation in mapping replacement values
- Extend HttpResourceNames and HttpResourceDecorator with overloaded methods accepting CharSequence host
- Pass host from HttpClientDecorator and UrlInstrumentation to HTTP_RESOURCE_DECORATOR
- Maintain 100% backward compatibility for existing path-only mappings
- Add unit tests for AntPatternHttpPathNormalizer, HttpResourceDecorator, and HttpClientDecorator

Fixes DataDog#6886
@RahulPrasanna-code
RahulPrasanna-code requested review from a team as code owners October 25, 2026 08:55
@RahulPrasanna-code
RahulPrasanna-code requested review from PerfectSlayer and jordan-wong and removed request for a team October 25, 2026 08:55
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T09:02:22.104742Z 82f55dd PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 82f55ddede

ℹ️ 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".


public final void withServerPath(
AgentSpan span, CharSequence method, CharSequence path, boolean encoded) {
withServerPath(span, method, null, path, encoded);

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 Pass the server host to the new overload

When a host-prefixed DD_TRACE_HTTP_SERVER_PATH_RESOURCE_NAME_MAPPING is configured, every normal server span still enters this overload and gets host = null: the only production call found is HttpServerDecorator.onRequest at line 365, even though URIDataAdapter.host() is already available there. Consequently the new server matcher can never evaluate host + path, so server host rules always fall back to path normalization; pass the effective request host to the five-argument overload.

Useful? React with 👍 / 👎.

Comment on lines +73 to +76
if (pattern.startsWith("http://")) {
pattern = pattern.substring(7);
} else if (pattern.startsWith("https://")) {
pattern = pattern.substring(8);

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 Parse scheme-prefixed mapping keys before stripping them

When a user configures the newly supported form https://secure.com/api/*:secure_api, it never reaches this normalization: both path-resource-name mappings are loaded by ConfigProvider.getOrderedMap, and ConfigConverter.loadMap treats the first : as the key/value separator, producing key https and value //secure.com/api/*:secure_api. Thus the scheme-stripping branch is unreachable for real configuration (the added test constructs the Map directly), and the rule never matches; the configuration parser needs an escaping or scheme-aware representation.

Useful? React with 👍 / 👎.


public static Pair<CharSequence, Byte> computeForClient(
CharSequence method, @Nonnull CharSequence path, boolean encoded) {
return computeForClient(method, null, path, encoded);

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 Preserve the URL host in tag-derived resource naming

When a manual/API or OTel client span derives its resource from http.url/url.full, TagInterceptor.setResourceFromUrl parses the complete URI but passes only its path to computeForClient (TagInterceptor.java:303-317), and this newly added delegation then forces the host to null. A host-prefixed client rule therefore works for HttpClientDecorator spans but silently fails on the existing tag-interceptor normalization path; that caller should extract the parsed URI host and invoke the host-aware overload.

Useful? React with 👍 / 👎.


boolean hasHost = host != null && !host.isEmpty();
if (hasHost) {
String hostAndPath = path.startsWith("/") ? (host + path) : (host + "/" + path);

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 Match hostnames without case sensitivity

When an outbound URI contains a mixed- or upper-case host such as https://AUTH.Example.COM/oauth2/token while the mapping uses the normal lower-case DNS spelling, URI.getHost() preserves the original case and this concatenates it directly into the case-sensitive AntPathMatcher. Although hostnames are case-insensitive, exact and wildcard rules such as auth.example.com/... will not match and the span falls back to the generic path, so normalize the configured and runtime host portions to a common case while preserving path case.

Useful? React with 👍 / 👎.

@datadog-datadog-prod-us1-2 datadog-datadog-prod-us1-2 Bot 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.

Bits Code Review: FAIL

Host-aware server mappings never receive the request host, so host-prefixed rules cannot match and host placeholders expand to an empty string. Existing path mappings beginning with double slashes also stop matching.

Open Bits AI session

🤖 Bits Code Review · Commit 82f55dd


public final void withServerPath(
AgentSpan span, CharSequence method, CharSequence path, boolean encoded) {
withServerPath(span, method, null, path, encoded);

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.

P2 Pass the request host into server resource normalization

HttpServerDecorator.onRequest still calls this overload, which supplies a null host despite having url.host() available. Consequently, host-prefixed server mappings never match, and server replacements containing {host} receive an empty string. Update the server request call site to pass the host into the new overload.

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

if (pattern.startsWith("http://")) {
pattern = pattern.substring(7);
} else if (pattern.startsWith("https://")) {
pattern = pattern.substring(8);

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.

P2 Handle scheme prefixes in configuration parsing

Configuring https://secure.com/api/*:secure_api does not reach this scheme-handling branch: ConfigConverter.parseOrderedMap splits at the first colon, producing the key https and value //secure.com/api/*:secure_api. Requests therefore silently use fallback normalization. The new test constructs the map directly and bypasses configuration parsing; scheme support also requires updating the configuration parsing path.

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

} else if (pattern.startsWith("https://")) {
pattern = pattern.substring(8);
} else if (pattern.startsWith("//")) {
pattern = pattern.substring(2);

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.

P2 Preserve existing double-slash path mappings

An existing path-only mapping such as //foo/:mapped previously matched the request path //foo/123. Stripping its leading slashes changes the pattern to foo/, which AntPathMatcher rejects against an absolute path. Preserve the original pattern alongside the host-pattern interpretation so existing resource mappings continue working.

Suggested change
pattern = pattern.substring(2);
resourceNameMatchers.put(pattern, entry.getValue());
pattern = pattern.substring(2);

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

String hostAndPath = path.startsWith("/") ? (host + path) : (host + "/" + path);
for (Map.Entry<String, String> entry : resourceNameMatchers.entrySet()) {
String pattern = entry.getKey();
if (!pattern.startsWith("/") && matcher.match(pattern, hostAndPath)) {

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.

P2 Match HTTP hostnames case-insensitively

For https://AUTH.EXAMPLE.COM/oauth2/token, URI.getHost() preserves uppercase characters, but AntPathMatcher compares them case-sensitively. The rule auth.example.com/oauth2/*:oauth therefore misses the same HTTP host solely because its casing differs, causing fallback resource names. Normalize the hostname portions of both configured patterns and request inputs while preserving path case.

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

This branch has not been deployed

No deployments
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.

1 participant