Java Trace SDK

Using the Instana Java Trace SDK (github), it is possible to manually instrument Entries and Exits that Instana does not yet know, as well as markup interesting sections of custom code. It also supports creating custom correlation across binary protocols and adding custom defined key value pairs to spans (for extracting relevant information).

Exits and Entries for a Custom Binary Protocol

The published example app (https://github.com/instana/instana-java-sdk/tree/master/instana-java-sdk-sample) contains a CustomTCP server and client, illustrating how the SDK can be used for marking entries, exits, and performing correlations.

Using the instana-java-sdk

The Java Trace SDK requires you to add a small jar file to your application. This jar file contains the annotations and helper classes used to markup the sections of code that Instana should handle. When using Maven, the dependency can be easily added with:

<dependency>
  <groupId>com.instana</groupId>
  <artifactId>instana-java-sdk</artifactId>
  <version>1.2.0</version>
</dependency>

The instana-java-sdk artifact is available from the default Maven central repository.

When no agent is monitoring the JVM, which includes the above library and has sections marked with annotations, then they act as No-Op. It is safe to use the annotations, they do not have any impact at all as long as no agent is monitoring the JVM. To have an Instana agent actually make use of the annotations, you have to tell it which Java package names have those annotations. This is because scanning for annotations is a heavyweight process, and scanning the complete classpath of big applications can take a long time.

The required configuration.yaml section looks like this:

# Java Tracing
com.instana.plugin.javatrace:
  instrumentation:
    # By default no packages are scanned for SDK annotations.
    sdk:
      packages:
        - 'com.mycompany.backend'
        - 'com.mycompany.frontend'

The packages are scanned recursively. That is, if the com.mycompany.backend is configured to be scanned for annotations, com.mycompany.backend.impl and other sub-packages will be scanned as well.

Marking an Intermediate

To mark a method in an Intermediat span, just add the following annotation:

@Span(value = "custom-tcp-server")

Intermediate is the default type of SDK spans and used if not stated otherwise. This kind of spans mark "interesting" methods in your application that you want to have better visibility into. Please keep in mind that this is no substitution for a real profiler, like our Instana AutoProfile™.

Marking an Entry

To mark a method in an Entry span, just add the following annotation, as shown in this example:

@Span(type = Type.ENTRY, value = "custom-tcp-server")

As soon as Instana sees code entering this method, a trace is started. Entry spans normally indicate incoming calls from external systems or scheduled tasks.

Marking an Exit

To mark a method in an Exit span, just add the following annotation, as shown in this example:

@Span(type = Type.EXIT, value = "custom-tcp-client", capturedStackFrames = 5)

Whenever this method is encountered, Instana will record a span labeling this as an Exit call. Exits mark outgoing calls to external systems.

Correlating Exit and Entry

In many cases where an Exit and Entry are not yet known to Instana, applications use them to perform some kind of remote communication. When an Exit is encountered and no correlation is performed, the trace "breaks," meaning two traces would be observed, one ending at the Exit, and a new one starting at the Entry.

It is possible to help Instana perform a correlation by manually transporting correlation information across the remote communication. On the exit side, the exit annotation will make sure that the required correlation identifiers exist. One can simply invoke (https://github.com/instana/instana-java-sdk/blob/master/instana-java-sdk/src/main/java/com/instana/sdk/support/SpanSupport.java#L182):

SpanSupport.addTraceHeadersIfTracing(Type.EXIT, params);

to fill the given params map with the required IDs. Should the protocol support other means of transporting other data, it is possible to get the IDs with:

currentTraceId(type)

currentSpanId(type)

On the entry side, it is important to read the parameters and set them for the Entry Span, before the span is created by annotations:

(https://github.com/instana/instana-java-sdk/blob/master/instana-java-sdk-sample/custom-http-sample/src/main/java/com/instana/sample/GreetingHandler.java#L52):

SpanSupport.inheritNext(SpanSupport.stringAsId(trace), SpanSupport.stringAsId(span));

By doing so, the Entry Span will automatically join the remote trace and add itself as child span of the calling Exit.

Conversion and Naming

In certain situations it might be beneficial to transform SDK spans into a different type such as HTTP or RPC, to better represent the origin of these spans. This can be done by adding conversion tags to the spans as described in the processing tags section of the best practices for custom tracing.

These tags can be set using the SpanSupport.annotate(type, name, key, value) method.

Instana will treat these tags the way automatic agent instrumentation works as soon as these fields are nested under the tags. key. So, for example, to set the http.url field, the method call would look like this:

SpanSupport.annotate(<type>, <name>, "tags.http.url", "service://my-awesome-service/some/path")

In the same manner it is also possible to change the service, endpoint and call name as described on the best practices for custom tracing page.

When no specific fields are provided, the name of the service for all SDK spans will be SDK. The endpoint and call name will be the given name in either the @Span annotation or the SpanSupport.annotate() method.

Saving and Restoring Trace Context

The ContextSupport class enables the current trace context to be saved and restored. The takeSnapshot() method stores trace context information, such as trace id and span id, in an internal map. The restoreSnapshot() method reads the information that was saved by the takeSnapshot() method and uses it to set the current trace context. These methods are useful in situations where an application uses asynchronous processing to complete a task and the trace needs to be continued in another thread.

For example, the current trace context can be saved with:

snapShotKey = ContextSupport.takeSnapshot();

Then, the trace context can be restored in another thread with:

ContextSupport.restoreSnapshot(snapShotKey);

Tutorials

The following tutorials will guide you through some of the common use-cases for the Java Trace SDK:

Troubleshooting

Should the SDK traces not show up in the UI, several items are worth checking:

Why do captured parameter and return values only show class names and hashed memory addresses?

The instrumentation will call toString() on each captured parameter and return value. This means that the values visible within Instana directly depend on the specific toString() method implementations of the parameter and return value classes.

When toString() is not overridden, then java.lang.Object#toString() is eventually used. This method returns getClass().getName() + '@' + Integer.toHexString(hashCode()) and results in the format you are seeing.

To improve visibility into captured parameters and return values, we recommend to properly override toString().

Why are return values sometimes not available?

Return values may not be available even though captureReturn=true is set, when…