Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont
if (maybeSpan.isPresent()) {
Span span = maybeSpan.get();
headers.putSingle(TraceHttpHeaders.TRACE_ID, span.getTraceId());
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It's pretty funky that this filter is called twice in the case of a failing streaming response... indicates to me that we don't fully understand the jersey behavior here.

ignoring our ignorance for a moment, the fix seems ok to me, but i'd like to see a comment indicating why this else block exists and what it does and how.

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.

Agreed, will do

// When the filter is called twice (e.g. an exception is thrown in a streaming call),
// the current trace will be empty. To allow clients to still get the trace ID corresponding to
// the failure, we retrieve it from the requestContext.
Optional.ofNullable(requestContext.getProperty(TRACE_ID_PROPERTY_NAME))
.ifPresent(s -> headers.putSingle(TraceHttpHeaders.TRACE_ID, s));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.client.JerseyClientBuilder;
import org.junit.After;
Expand Down Expand Up @@ -156,6 +157,36 @@ public void testTraceState_withoutRequestHeadersGeneratesValidTraceResponseHeade
assertThat(spanCaptor.getValue().getOperation(), is("GET /trace"));
}

@Test
public void testTraceState_withoutRequestHeadersGeneratesValidTraceResponseHeadersWhenFailing() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

note that this test succeeded before this fix

Response response = target.path("/failing-trace").request().get();
assertThat(response.getHeaderString(TraceHttpHeaders.TRACE_ID), not(nullValue()));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

taking a step back, it's not clear to me that the response needs to contain a trace id header at all (even in the success case).

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.

In the general case of streaming one? I'm confused, this is how it's been working so far everywhere. If not provided on the request headers, a trace will be created and returned so that you can find out what happens. This makes even more sense IMHO when things go wrong.

For what it's worth, here even forcing the trace with request headers is ignored in the failing streaming case. Doesn't really matter if you force it or let it pick, the exception handling breaks it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cool, that sounds reasonable.

assertThat(response.getHeaderString(TraceHttpHeaders.PARENT_SPAN_ID), is(nullValue()));
assertThat(response.getHeaderString(TraceHttpHeaders.SPAN_ID), is(nullValue()));
verify(observer).consume(spanCaptor.capture());
assertThat(spanCaptor.getValue().getOperation(), is("GET /failing-trace"));
}

@Test
public void testTraceState_withoutRequestHeadersGeneratesValidTraceResponseHeadersWhenStreaming() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

note that this test succeeded before this fix

Response response = target.path("/streaming-trace").request().get();
assertThat(response.getHeaderString(TraceHttpHeaders.TRACE_ID), not(nullValue()));
assertThat(response.getHeaderString(TraceHttpHeaders.PARENT_SPAN_ID), is(nullValue()));
assertThat(response.getHeaderString(TraceHttpHeaders.SPAN_ID), is(nullValue()));
verify(observer).consume(spanCaptor.capture());
assertThat(spanCaptor.getValue().getOperation(), is("GET /streaming-trace"));
}

@Test
public void testTraceState_withoutRequestHeadersGeneratesValidTraceResponseHeadersWhenFailingToStream() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this test fails before this fix, and succeeds with this fix

Response response = target.path("/failing-streaming-trace").request().get();
assertThat(response.getHeaderString(TraceHttpHeaders.TRACE_ID), not(nullValue()));
assertThat(response.getHeaderString(TraceHttpHeaders.PARENT_SPAN_ID), is(nullValue()));
assertThat(response.getHeaderString(TraceHttpHeaders.SPAN_ID), is(nullValue()));
verify(observer).consume(spanCaptor.capture());
assertThat(spanCaptor.getValue().getOperation(), is("GET /failing-streaming-trace"));
}

@Test
public void testTraceState_withSamplingHeaderWithoutTraceIdDoesNotUseTraceSampler() {
target.path("/trace").request()
Expand Down Expand Up @@ -216,13 +247,33 @@ public final void run(Configuration config, final Environment env) throws Except

public static final class TracingTestResource implements TracingTestService {
@Override
public void getTraceOperation() {}
public void getTraceOperation() {
throw new RuntimeException("FAIL");
}

@Override
public void postTraceOperation() {}

@Override
public void getTraceWithPathParam() {}

@Override
public void getFailingTraceOperation() {
throw new RuntimeException();
}

@Override
public StreamingOutput getFailingStreamingTraceOperation() {
return os -> {
throw new RuntimeException();
};
}

@Override
public StreamingOutput getStreamingTraceOperation() {
return os -> {
};
}
}

@Path("/")
Expand All @@ -240,5 +291,19 @@ public interface TracingTestService {
@GET
@Path("/trace/{param}")
void getTraceWithPathParam();

@GET
@Path("/failing-trace")
void getFailingTraceOperation();

@GET
@Path("/failing-streaming-trace")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
StreamingOutput getFailingStreamingTraceOperation();

@GET
@Path("/streaming-trace")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
StreamingOutput getStreamingTraceOperation();
}
}