Tutorial: Instrumenting a Custom Java HTTP Framework with the Instana Tracing SDK
The Instana agent supports many Java HTTP frameworks, and in most cases tracing will be supported out-of-the-box as soon as you run the Java agent.
However, in case you have a custom in-house HTTP server that is not supported, you can use the Java Trace SDK to add tracing for your framework.
This tutorial shows how to do this.
- Example Code
- Enable Tracing for the Custom HTTP Server
- Handling Incoming Tracing Headers
- Adding Tags
Example Code
The example code for the custom-http-sample
tutorial can be found on github.com. It implements the following scenario:
If you run the example and open http://localhost:8080, you will get the response Hello, Stan!
. However, if you analyze the calls in Instana, you will see that the trace is broken.
Trace 1:
Trace 2:
The outgoing requests are implemented using the Apache HTTP client, which is supported by Instana's automatic tracing. The missing link is the entry span (incoming call) GET /greeting
at the custom HTTP server.
Enable Tracing for the Custom HTTP Server
For supported frameworks, the Instana agent instruments Java applications automatically. For the custom HTTP server, we need to add some annotations to tell the Instana agent which method calls should start a new span.
First, add mvn dependency
<dependency>
<groupId>com.instana</groupId>
<artifactId>instana-java-sdk</artifactId>
<version>1.2.0</version>
</dependency>
Second, add a @Span
annotation to the method handling the HTTP request in the custom HTTP server.
@Span(type = Span.Type.ENTRY, value = "my-custom-http-server")
public String apply(Map<String, String> headers) {
// ...
}
This annotation tells the Java agent to generate an ENTRY
span (aka. server span) whenever this method is called.
Third, add the following to the Instana agent's configuration.yaml
file to tell it that Java classes in package com.instana.sample.*
should be scanned for the @Span
annotation:
com.instana.plugin.javatrace:
instrumentation:
sdk:
packages:
- 'com.instana.sample'
Now, the custom server gets instrumented, and you see that the outgoing call to /name
is triggered from the incoming call to /greeting
.
However, the trace is still broken, because the trace ID is not passed on between the incoming call and the outgoing call.
Handling Incoming Tracing Headers
Instana uses the following HTTP headers to pass tracing information from one service to the next:
X-INSTANA-T
: Trace IDX-INSTANA-S
: Parent span IDX-INSTANA-L
: Level0
means the trace should be suppressed, which might be useful for health checks
In order to correlate the trace context in the customer HTTP server, you need to add the following method to GreetingHandler
:
private void correlateTracing(Map<String, String> request) {
String level = request.remove(SpanSupport.LEVEL);
String traceId = request.remove(SpanSupport.TRACE_ID);
String parentSpanId = request.remove(SpanSupport.SPAN_ID);
if (SpanSupport.SUPPRESS.equals(level)) {
SpanSupport.suppressNext();
} else if (traceId != null && parentSpanId != null) {
SpanSupport.inheritNext(traceId, parentSpanId);
}
}
Note that we remove the headers after evaluating them, leaving only the original headers as they would be sent without Instana tracing.
However, you cannot just call correlateTracing()
in apply()
, because the headers need to be evaluated before the method with the @Span
annotation is called. As a workaround, you could rename the apply()
method to doApply()
and add a step as follows:
@Override
public String apply(Map<String, String> headers) {
correlateTracing(headers);
return doApply(headers);
}
@Span(type = Span.Type.ENTRY, value = "my-custom-http-server")
private String doApply(Map<String, String> headers) {
// ...
}
When you now run curl http://localhost:8080
, you will see the complete Trace in Instana