Skip to content

Commit 0ae82bc

Browse files
jandro996devflow.devflow-routing-intake
andauthored
Fix HttpEndpointPostProcessor incorrectly overwriting span resource name (#10754)
Fix HttpEndpointPostProcessor incorrectly overwriting span resource name Per RFC-1051, the trace resource renaming feature is scoped to enriching APM stats buckets with HTTPMethod/HTTPEndpoint fields and tagging service entry spans with http.endpoint when computed from the URL. Resource name renaming on the span itself is the backend's responsibility. The HttpEndpointPostProcessor was calling setResourceName() with the inferred endpoint, which caused the top-level span's resource field to be overwritten in the agent — behaviour not specified by the RFC. Remove the setResourceName() call and the now-unused HTTP_SERVER_RESOURCE_RENAMING priority constant. Update tests to assert that the resource name is never modified and that http.endpoint is correctly set in the tags when the route is ineligible or absent. Merge branch 'master' into alejandro.gonzalez/fix-resourceName-bug WIP Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 35cee76 commit 0ae82bc

File tree

3 files changed

+37
-67
lines changed

3 files changed

+37
-67
lines changed

dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/HttpEndpointPostProcessor.java

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,28 @@
66

77
import datadog.trace.api.TagMap;
88
import datadog.trace.api.endpoint.EndpointResolver;
9-
import datadog.trace.api.normalize.HttpResourceNames;
109
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
11-
import datadog.trace.bootstrap.instrumentation.api.ResourceNamePriorities;
1210
import datadog.trace.core.DDSpanContext;
1311
import java.util.List;
1412
import org.slf4j.Logger;
1513
import org.slf4j.LoggerFactory;
1614

1715
/**
18-
* Post-processes HTTP spans to update resource names based on inferred endpoints.
16+
* Post-processes HTTP spans to resolve and tag the {@code http.endpoint} for APM trace metrics.
1917
*
20-
* <p>This processor implements the trace resource renaming feature by:
18+
* <p>Implements the endpoint inference strategy from RFC-1051: tags the span with {@code
19+
* http.endpoint} (computed from the URL) so that stats buckets can be aggregated per endpoint. This
20+
* processor does <em>not</em> overwrite the span's resource name — resource renaming is the
21+
* backend's responsibility.
2122
*
22-
* <ul>
23-
* <li>Using EndpointResolver to determine the best endpoint (route or simplified URL)
24-
* <li>Combining HTTP method with the endpoint to create a resource name (e.g., "GET
25-
* /users/{param:int}")
26-
* <li>Updating the span's resource name only when an endpoint is available
27-
* </ul>
28-
*
29-
* <p>The processor respects the endpoint resolution logic:
23+
* <p>Resolution logic:
3024
*
3125
* <ul>
32-
* <li>If alwaysSimplifiedEndpoint=true, always compute from URL
33-
* <li>If http.route exists and is eligible, use it
34-
* <li>Otherwise, compute simplified endpoint from URL
26+
* <li>If {@code http.route} is eligible, use it as the endpoint (no {@code http.endpoint} tag
27+
* added)
28+
* <li>Otherwise, compute a simplified endpoint from {@code http.url} and tag the span with {@code
29+
* http.endpoint}
30+
* <li>If {@code alwaysSimplifiedEndpoint=true}, always compute from URL regardless of route
3531
* </ul>
3632
*/
3733
public class HttpEndpointPostProcessor extends TagsPostProcessor {
@@ -66,47 +62,14 @@ public void processTags(
6662
return;
6763
}
6864

69-
// Extract HTTP tags
70-
Object httpMethodObj = unsafeTags.get(HTTP_METHOD);
71-
Object httpRouteObj = unsafeTags.get(HTTP_ROUTE);
72-
Object httpUrlObj = unsafeTags.get(HTTP_URL);
73-
74-
log.debug(
75-
"Processing tags for span {}: httpMethod={}, httpRoute={}, httpUrl={}",
76-
spanContext.getSpanId(),
77-
httpMethodObj,
78-
httpRouteObj,
79-
httpUrlObj);
80-
81-
if (httpMethodObj == null) {
82-
// Not an HTTP span, skip processing
83-
log.debug("No HTTP method found, skipping HTTP endpoint post processing");
65+
if (unsafeTags.getObject(HTTP_METHOD) == null) {
8466
return;
8567
}
8668

8769
try {
88-
String httpMethod = httpMethodObj.toString();
89-
String httpRoute = httpRouteObj != null ? httpRouteObj.toString() : null;
90-
String httpUrl = httpUrlObj != null ? httpUrlObj.toString() : null;
91-
92-
// Resolve endpoint using EndpointResolver
93-
// Pass unsafeTags directly - it's safe to use at this point since span is finished
94-
String endpoint = endpointResolver.resolveEndpoint(unsafeTags, httpRoute, httpUrl);
95-
96-
if (endpoint != null && !endpoint.isEmpty()) {
97-
// Combine method and endpoint into resource name using cached join
98-
CharSequence resourceName = HttpResourceNames.join(httpMethod, endpoint);
99-
spanContext.setResourceName(
100-
resourceName, ResourceNamePriorities.HTTP_SERVER_RESOURCE_RENAMING);
101-
102-
log.debug(
103-
"Updated resource name to '{}' for span {} (method={}, route={}, url={})",
104-
resourceName,
105-
spanContext.getSpanId(),
106-
httpMethod,
107-
httpRoute,
108-
httpUrl);
109-
}
70+
String httpRoute = unsafeTags.getString(HTTP_ROUTE);
71+
String httpUrl = unsafeTags.getString(HTTP_URL);
72+
endpointResolver.resolveEndpoint(unsafeTags, httpRoute, httpUrl);
11073
} catch (Throwable t) {
11174
log.debug("Error processing HTTP endpoint for span {}", spanContext.getSpanId(), t);
11275
}

dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/HttpEndpointPostProcessorTest.groovy

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
package datadog.trace.core.tagprocessor
22

3-
import datadog.trace.bootstrap.instrumentation.api.ResourceNamePriorities
3+
import datadog.trace.api.TagMap
44
import datadog.trace.bootstrap.instrumentation.api.Tags
55
import datadog.trace.core.DDSpanContext
66
import datadog.trace.api.endpoint.EndpointResolver
77
import spock.lang.Specification
88

99
class HttpEndpointPostProcessorTest extends Specification {
1010

11-
def "should update resource name with endpoint when enabled"() {
11+
def "should not overwrite resource name when http.route is available and eligible"() {
12+
// RFC-1051: the processor enriches stats buckets and tags the span with http.endpoint,
13+
// but must NOT overwrite the span's resourceName — that is the backend's responsibility.
1214
given:
1315
def endpointResolver = new EndpointResolver(true, false)
1416
def processor = new HttpEndpointPostProcessor(endpointResolver)
1517
def mockContext = Mock(DDSpanContext)
16-
def tags = [
18+
def tags = TagMap.fromMap([
1719
(Tags.HTTP_METHOD): "GET",
1820
(Tags.HTTP_ROUTE): "/greeting",
1921
(Tags.HTTP_URL): "http://localhost:8080/greeting"
20-
]
22+
])
2123

2224
when:
2325
processor.processTags(tags, mockContext, [])
2426

2527
then:
26-
1 * mockContext.setResourceName({ it.toString() == "GET /greeting" }, ResourceNamePriorities.HTTP_SERVER_RESOURCE_RENAMING)
28+
0 * mockContext.setResourceName(_, _)
29+
// http.route is eligible — no http.endpoint tag should be added
30+
!tags.containsKey(Tags.HTTP_ENDPOINT)
2731
}
2832

29-
def "should compute simplified endpoint from URL when route is invalid"() {
33+
def "should compute and tag http.endpoint from URL when route is invalid, without touching resourceName"() {
3034
given:
3135
def endpointResolver = new EndpointResolver(true, false)
3236
def processor = new HttpEndpointPostProcessor(endpointResolver)
3337
def mockContext = Mock(DDSpanContext)
34-
def tags = [
38+
def tags = TagMap.fromMap([
3539
(Tags.HTTP_METHOD): "GET",
36-
(Tags.HTTP_ROUTE): "*", // Invalid route
40+
(Tags.HTTP_ROUTE): "*", // catch-all — ineligible per RFC-1051
3741
(Tags.HTTP_URL): "http://localhost:8080/users/123/orders/456"
38-
]
42+
])
3943

4044
when:
4145
processor.processTags(tags, mockContext, [])
4246

4347
then:
44-
1 * mockContext.setResourceName({ it.toString() == "GET /users/{param:int}/orders/{param:int}" }, ResourceNamePriorities.HTTP_SERVER_RESOURCE_RENAMING)
48+
0 * mockContext.setResourceName(_, _)
49+
tags[Tags.HTTP_ENDPOINT] == "/users/{param:int}/orders/{param:int}"
4550
}
4651

4752
def "should skip non-HTTP spans"() {
@@ -58,6 +63,7 @@ class HttpEndpointPostProcessorTest extends Specification {
5863

5964
then:
6065
0 * mockContext.setResourceName(_, _)
66+
!tags.containsKey(Tags.HTTP_ENDPOINT)
6167
}
6268

6369
def "should not process when resource renaming is disabled"() {
@@ -75,23 +81,25 @@ class HttpEndpointPostProcessorTest extends Specification {
7581

7682
then:
7783
0 * mockContext.setResourceName(_, _)
84+
!tags.containsKey(Tags.HTTP_ENDPOINT)
7885
}
7986

80-
def "should use simplified endpoint when alwaysSimplified is true"() {
87+
def "should tag http.endpoint from URL even when alwaysSimplified is true, without touching resourceName"() {
8188
given:
8289
def endpointResolver = new EndpointResolver(true, true)
8390
def processor = new HttpEndpointPostProcessor(endpointResolver)
8491
def mockContext = Mock(DDSpanContext)
85-
def tags = [
92+
def tags = TagMap.fromMap([
8693
(Tags.HTTP_METHOD): "GET",
8794
(Tags.HTTP_ROUTE): "/greeting",
8895
(Tags.HTTP_URL): "http://localhost:8080/users/123"
89-
]
96+
])
9097

9198
when:
9299
processor.processTags(tags, mockContext, [])
93100

94101
then:
95-
1 * mockContext.setResourceName({ it.toString() == "GET /users/{param:int}" }, ResourceNamePriorities.HTTP_SERVER_RESOURCE_RENAMING)
102+
0 * mockContext.setResourceName(_, _)
103+
tags[Tags.HTTP_ENDPOINT] == "/users/{param:int}"
96104
}
97105
}

internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/ResourceNamePriorities.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ public class ResourceNamePriorities {
66
public static final byte HTTP_404 = 2;
77
public static final byte HTTP_FRAMEWORK_ROUTE = 3;
88
public static final byte RPC_COMMAND_NAME = 3;
9-
public static final byte HTTP_SERVER_RESOURCE_RENAMING = 3;
109
public static final byte HTTP_SERVER_CONFIG_PATTERN_MATCH = 4;
1110
public static final byte HTTP_CLIENT_CONFIG_PATTERN_MATCH = 4;
1211
public static final byte TAG_INTERCEPTOR = 5;

0 commit comments

Comments
 (0)