Skip to content

Commit 9eecbc7

Browse files
authored
Tracing uniquely identifies requests (#574)
Tracing uniquely identifies requests
1 parent 868ab17 commit 9eecbc7

File tree

20 files changed

+304
-86
lines changed

20 files changed

+304
-86
lines changed

.palantir/revapi.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
acceptedBreaks:
2+
"4.5.2":
3+
com.palantir.tracing:tracing:
4+
- code: "java.method.addedToInterface"
5+
new: "method java.util.Optional<java.lang.String> com.palantir.tracing.TraceMetadata::getRequestId()"
6+
justification: "immutables are not for extension"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type: improvement
2+
improvement:
3+
description: Tracing uniquely identifies requests with request metadata and a `_requestId`
4+
MDC property.
5+
links:
6+
- https://github.com/palantir/tracing-java/pull/574

tracing-jersey/src/main/java/com/palantir/tracing/jersey/TraceEnrichingFilter.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.common.base.Strings;
2020
import com.palantir.tracing.Observability;
21+
import com.palantir.tracing.TraceMetadata;
2122
import com.palantir.tracing.Tracer;
2223
import com.palantir.tracing.Tracers;
2324
import com.palantir.tracing.api.Span;
@@ -45,6 +46,8 @@ public final class TraceEnrichingFilter implements ContainerRequestFilter, Conta
4546
/** This is the name of the trace id property we set on {@link ContainerRequestContext}. */
4647
public static final String TRACE_ID_PROPERTY_NAME = "com.palantir.tracing.traceId";
4748

49+
public static final String REQUEST_ID_PROPERTY_NAME = "com.palantir.tracing.requestId";
50+
4851
public static final String SAMPLED_PROPERTY_NAME = "com.palantir.tracing.sampled";
4952

5053
@Context
@@ -67,21 +70,26 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
6770
// Set up thread-local span that inherits state from HTTP headers
6871
if (Strings.isNullOrEmpty(traceId)) {
6972
// HTTP request did not indicate a trace; initialize trace state and create a span.
70-
Tracer.initTrace(getObservabilityFromHeader(requestContext), Tracers.randomId());
71-
Tracer.fastStartSpan(operation, SpanType.SERVER_INCOMING);
73+
Tracer.initTraceWithSpan(
74+
getObservabilityFromHeader(requestContext),
75+
Tracers.randomId(),
76+
operation,
77+
SpanType.SERVER_INCOMING);
78+
} else if (spanId == null) {
79+
Tracer.initTraceWithSpan(
80+
getObservabilityFromHeader(requestContext), traceId, operation, SpanType.SERVER_INCOMING);
7281
} else {
73-
Tracer.initTrace(getObservabilityFromHeader(requestContext), traceId);
74-
if (spanId == null) {
75-
Tracer.fastStartSpan(operation, SpanType.SERVER_INCOMING);
76-
} else {
77-
// caller's span is this span's parent.
78-
Tracer.fastStartSpan(operation, spanId, SpanType.SERVER_INCOMING);
79-
}
82+
// caller's span is this span's parent.
83+
Tracer.initTraceWithSpan(
84+
getObservabilityFromHeader(requestContext), traceId, operation, spanId, SpanType.SERVER_INCOMING);
8085
}
8186

8287
// Give asynchronous downstream handlers access to the trace id
8388
requestContext.setProperty(TRACE_ID_PROPERTY_NAME, Tracer.getTraceId());
8489
requestContext.setProperty(SAMPLED_PROPERTY_NAME, Tracer.isTraceObservable());
90+
Tracer.maybeGetTraceMetadata()
91+
.flatMap(TraceMetadata::getRequestId)
92+
.ifPresent(requestId -> requestContext.setProperty(REQUEST_ID_PROPERTY_NAME, requestId));
8593
}
8694

8795
// Handles outgoing response

tracing-jersey/src/test/java/com/palantir/tracing/jersey/TraceEnrichingFilterTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ public void testFilter_setsMdcIfTraceIdHeaderIsPresent() throws Exception {
225225
verify(request).setProperty(TraceEnrichingFilter.TRACE_ID_PROPERTY_NAME, "traceId");
226226
// Note: this will be set to a random value; we want to check whether the value is being set
227227
verify(request).setProperty(eq(TraceEnrichingFilter.SAMPLED_PROPERTY_NAME), anyBoolean());
228+
verify(request).setProperty(eq(TraceEnrichingFilter.REQUEST_ID_PROPERTY_NAME), anyString());
228229
}
229230

230231
@Test
@@ -240,6 +241,7 @@ public void testFilter_setsMdcIfTraceIdHeaderIsNotePresent() throws Exception {
240241
TraceEnrichingFilter.INSTANCE.filter(request);
241242
assertThat(MDC.get(Tracers.TRACE_ID_KEY)).hasSize(16);
242243
verify(request).setProperty(eq(TraceEnrichingFilter.TRACE_ID_PROPERTY_NAME), anyString());
244+
verify(request).setProperty(eq(TraceEnrichingFilter.REQUEST_ID_PROPERTY_NAME), anyString());
243245
}
244246

245247
public static class TracingTestServer extends Application<Configuration> {

tracing-okhttp3/src/test/java/com/palantir/tracing/okhttp3/OkhttpTraceInterceptorTest.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ public void before() {
6767

6868
@After
6969
public void after() {
70-
Tracer.initTrace(Observability.SAMPLE, Tracers.randomId());
70+
Tracer.initTraceWithSpan(Observability.SAMPLE, Tracers.randomId(), "op", SpanType.LOCAL);
7171
Tracer.unsubscribe("");
7272
}
7373

7474
@Test
7575
public void testPopulatesNewTrace_whenNoTraceIsPresentInGlobalState() throws IOException {
76+
Tracer.getAndClearTrace();
7677
OkhttpTraceInterceptor.INSTANCE.intercept(chain);
7778
verify(chain).request();
7879
verify(chain).proceed(requestCaptor.capture());
@@ -88,7 +89,7 @@ public void testPopulatesNewTrace_whenNoTraceIsPresentInGlobalState() throws IOE
8889
@Test
8990
public void testPopulatesNewTrace_whenParentTraceIsPresent() throws IOException {
9091
String originatingSpanId = "originating Span";
91-
OpenSpan parentState = Tracer.startSpan("operation", originatingSpanId, SpanType.SERVER_INCOMING);
92+
Tracer.initTraceWithSpan(Observability.SAMPLE, "id", "operation", originatingSpanId, SpanType.SERVER_INCOMING);
9293
String traceId = Tracer.getTraceId();
9394
try {
9495
OkhttpTraceInterceptor.INSTANCE.intercept(chain);
@@ -102,15 +103,13 @@ public void testPopulatesNewTrace_whenParentTraceIsPresent() throws IOException
102103

103104
Request intercepted = requestCaptor.getValue();
104105
assertThat(intercepted.headers(TraceHttpHeaders.SPAN_ID)).isNotNull();
105-
assertThat(intercepted.headers(TraceHttpHeaders.SPAN_ID)).doesNotContain(parentState.getSpanId());
106106
assertThat(intercepted.headers(TraceHttpHeaders.TRACE_ID)).containsOnly(traceId);
107-
assertThat(intercepted.headers(TraceHttpHeaders.PARENT_SPAN_ID)).containsOnly(parentState.getSpanId());
108107
assertThat(intercepted.headers(TraceHttpHeaders.ORIGINATING_SPAN_ID)).containsOnly(originatingSpanId);
109108
}
110109

111110
@Test
112111
public void testAddsIsSampledHeader_whenTraceIsObservable() throws IOException {
113-
Tracer.initTrace(Observability.SAMPLE, Tracers.randomId());
112+
Tracer.initTraceWithSpan(Observability.SAMPLE, Tracers.randomId(), "op", SpanType.LOCAL);
114113
OkhttpTraceInterceptor.INSTANCE.intercept(chain);
115114
verify(chain).proceed(requestCaptor.capture());
116115
assertThat(requestCaptor.getValue().headers(TraceHttpHeaders.IS_SAMPLED))
@@ -119,7 +118,7 @@ public void testAddsIsSampledHeader_whenTraceIsObservable() throws IOException {
119118

120119
@Test
121120
public void testHeaders_whenTraceIsNotObservable() throws IOException {
122-
Tracer.initTrace(Observability.DO_NOT_SAMPLE, Tracers.randomId());
121+
Tracer.initTraceWithSpan(Observability.DO_NOT_SAMPLE, Tracers.randomId(), "op", SpanType.LOCAL);
123122
String traceId = Tracer.getTraceId();
124123
OkhttpTraceInterceptor.INSTANCE.intercept(chain);
125124
verify(chain).proceed(requestCaptor.capture());
@@ -175,7 +174,7 @@ public void testHeaders_noMultiValue() throws IOException {
175174
.build();
176175
when(chain.request()).thenReturn(request);
177176

178-
Tracer.initTrace(Observability.SAMPLE, Tracers.randomId());
177+
Tracer.initTraceWithSpan(Observability.SAMPLE, Tracers.randomId(), "op", SpanType.LOCAL);
179178
OkhttpTraceInterceptor.INSTANCE.intercept(chain);
180179
verify(chain).proceed(requestCaptor.capture());
181180

tracing-undertow/src/main/java/com/palantir/tracing/undertow/TracingAttachments.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@ public final class TracingAttachments {
2424
/** Attachment to check whether the current request is being traced. */
2525
public static final AttachmentKey<Boolean> IS_SAMPLED = AttachmentKey.create(Boolean.class);
2626

27+
/** Attachment providing the request identifier. */
28+
public static final AttachmentKey<String> REQUEST_ID = AttachmentKey.create(String.class);
29+
2730
private TracingAttachments() {}
2831
}

tracing-undertow/src/main/java/com/palantir/tracing/undertow/UndertowTracing.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import com.google.common.annotations.VisibleForTesting;
2020
import com.google.common.base.Strings;
21+
import com.palantir.logsafe.SafeArg;
22+
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
2123
import com.palantir.tracing.DetachedSpan;
2224
import com.palantir.tracing.InternalTracers;
2325
import com.palantir.tracing.Observability;
@@ -73,6 +75,11 @@ private static void setExchangeState(HttpServerExchange exchange, DetachedSpan d
7375
// Populate response before proceeding since later operations might commit the response.
7476
exchange.getResponseHeaders().put(TRACE_ID, traceId);
7577
exchange.putAttachment(TracingAttachments.IS_SAMPLED, InternalTracers.isSampled(detachedSpan));
78+
Optional<String> requestId = InternalTracers.getRequestId(detachedSpan);
79+
if (!requestId.isPresent()) {
80+
throw new SafeIllegalStateException("No requestId is set", SafeArg.of("span", detachedSpan));
81+
}
82+
exchange.putAttachment(TracingAttachments.REQUEST_ID, requestId.get());
7683
exchange.putAttachment(REQUEST_SPAN, detachedSpan);
7784
exchange.addExchangeCompleteListener(DetachedTraceCompletionListener.INSTANCE);
7885
}

tracing-undertow/src/test/java/com/palantir/tracing/undertow/TracedRequestHandlerTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import com.palantir.tracing.api.TraceHttpHeaders;
2929
import io.undertow.Undertow;
3030
import io.undertow.server.HttpHandler;
31-
import io.undertow.server.handlers.ResponseCodeHandler;
31+
import io.undertow.util.HttpString;
3232
import java.io.IOException;
3333
import java.net.HttpURLConnection;
3434
import java.net.URL;
@@ -70,7 +70,8 @@ public void before() {
7070

7171
traceReportedLatch = new CountDownLatch(1);
7272
port = portSelector.incrementAndGet();
73-
HttpHandler nextHandler = new TracedRequestHandler(ResponseCodeHandler.HANDLE_200);
73+
HttpHandler nextHandler = new TracedRequestHandler(exchange -> exchange.getResponseHeaders()
74+
.put(HttpString.tryFromString("requestId"), exchange.getAttachment(TracingAttachments.REQUEST_ID)));
7475
server = Undertow.builder()
7576
.addHttpListener(port, null)
7677
.setHandler(exchange -> {
@@ -101,6 +102,7 @@ public void testRequestTracing_sampled() throws Exception {
101102
con.setRequestProperty(TraceHttpHeaders.TRACE_ID, "1234");
102103
assertThat(con.getResponseCode()).isEqualTo(200);
103104
assertThat(con.getHeaderField(TraceHttpHeaders.TRACE_ID)).isEqualTo("1234");
105+
assertThat(con.getHeaderField("requestId")).isNotEmpty();
104106
assertThat(traceReportedLatch.await(5, TimeUnit.SECONDS)).isTrue();
105107
verifyNoMoreInteractions(traceSampler);
106108
verify(observer).consume(spanCaptor.capture());

tracing/src/main/java/com/palantir/tracing/DeferredTracer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public final class DeferredTracer implements Serializable {
6565
@Nullable
6666
private final String parentSpanId;
6767

68+
@Nullable
69+
private final transient String requestId;
70+
6871
/**
6972
* Deprecated.
7073
*
@@ -90,11 +93,13 @@ public DeferredTracer(String operation) {
9093
if (maybeTrace.isPresent()) {
9194
Trace trace = maybeTrace.get();
9295
this.traceId = trace.getTraceId();
96+
this.requestId = trace.getRequestId().orElse(null);
9397
this.isObservable = trace.isObservable();
9498
this.parentSpanId = trace.top().map(OpenSpan::getSpanId).orElse(null);
9599
this.operation = operation;
96100
} else {
97101
this.traceId = null;
102+
this.requestId = null;
98103
this.isObservable = false;
99104
this.parentSpanId = null;
100105
this.operation = null;
@@ -117,7 +122,7 @@ CloseableTrace withTrace() {
117122

118123
Optional<Trace> originalTrace = Tracer.copyTrace();
119124

120-
Tracer.setTrace(Trace.of(isObservable, traceId));
125+
Tracer.setTrace(Trace.of(isObservable, traceId, Optional.ofNullable(requestId)));
121126
if (parentSpanId != null) {
122127
Tracer.fastStartSpan(operation, parentSpanId, SpanType.LOCAL);
123128
} else {

tracing/src/main/java/com/palantir/tracing/InternalTracers.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.palantir.tracing;
1818

19+
import java.util.Optional;
20+
1921
/**
2022
* Internal utilities meant for consumption only inside of the tracing codebase.
2123
*
@@ -29,5 +31,10 @@ public static boolean isSampled(DetachedSpan detachedSpan) {
2931
return Tracer.isSampled(detachedSpan);
3032
}
3133

34+
/** Returns true if the provided detachedSpan is sampled. */
35+
public static Optional<String> getRequestId(DetachedSpan detachedSpan) {
36+
return Tracer.getRequestId(detachedSpan);
37+
}
38+
3239
private InternalTracers() {}
3340
}

0 commit comments

Comments
 (0)