Docs » µAPM Instrumentation Guide » Java Instrumentation

Java Instrumentation

Important

Before you start instrumenting your applications, review the information in Instrumentation Overview.

If you are new to tracing in Java, we recommend starting out with our Java agent that will automatically instrument many common libraries in your Java application with no code changes or recompilation required (only a change to how the application is invoked) – hence we refer to that as auto-instrumentation.

Java agents are jar files that implement a premain method and generally are used to modify classes as they are loaded. They use the standard java.lang.instrument package to do this and require no prior modification of the target application’s source code.

Our Java agent for tracing works by injecting span start/finish calls into specific, commonly used Java classes dynamically as they are loaded by the JVM. The end result is much the same as if you had manually placed those span start/finish calls directly in the code, with some slight overhead upon application startup to do the class bytecode alterations.

To use our Java agent, simply download it to the local filesystem of the target application and then invoke your application with the -javaagent flag. Assuming that you have the Smart Agent setup on the same host already, the logical steps are as follows:

$ sudo curl -sSL -o /opt/signalfx-tracing.jar 'https://search.maven.org/remote_content?g=com.signalfx.public&a=signalfx-java-agent&v=LATEST&c=unbundled'

$ # Change this to the common name of your app
$ export SFX_SERVICE_NAME=my-app

$ java -javaagent:/opt/signalfx-tracing.jar ...other Java flags for your app...

Your app invocation will probably look different from this, but those are the logical steps.

Kubernetes Deployment

When deploying the Java agent in a Kubernetes-run app, you need to configure the Java agent to send traces to an instance of the Smart Agent running on the same Kubernetes node. To do so, refer to the sample Kubernetes Deployment at Deploying on Kubernetes. The Java agent looks for the environment variable SFX_AGENT_HOST (which is already used in the example Deployment), which defaults to localhost, but will be overridden in this case to point to the underlying K8s node itself.

Built-in Instrumentations

Our Java agent comes with several instrumentation libraries built in. An up-to-date list is on the README for the Java agent Github repo.

Some of these instrumentations are disabled by default and must be enabled by specifying the JVM property flag -Ddd.integration.INTEGRATION_NAME.enabled=true where INTEGRATION_NAME is the name of the integration as shown in the library table in the Java agent Github repo.

OpenTracing Contributed Instrumentations

For the most part, our Java agent provides its own instrumentation logic and does not rely on instrumentation contributed to the OpenTracing project. However, you are also free to use standard OpenTracing instrumentations if you want. An up-to-date list of OpenTracing Java libraries is available on Github. All of these instrumentations are compatible with manual instrumentation because they are OpenTracing-compatible, and thus will use the same span scope/context that manual instrumentation uses. Note that the use of these requires code changes to your application and are not installed automatically by the Java agent.

Manual Instrumentation (with the Java Agent)

The Java agent provides a Jaeger tracer instance that is automatically configured upon startup. This tracer instance is available to your application code via the GlobalTracer class of the opentracing-utils artifact. Simply add the following to your Maven POM:

<dependency>
  <groupId>io.opentracing</groupId>
  <artifactId>opentracing-util</artifactId>
  <version>0.31.0</version>
  <scope>provided</scope>
</dependency>

Or to your Gradle config:

compileOnly group: 'io.opentracing', name: 'opentracing-util', version: '0.31.0'

The scope is provided in Maven and compileOnly in Gradle because that artifact is included in the Java agent and will be available to your application classes at runtime.

Our Java agent example shows the use of auto-instrumented libraries in conjunction with manually instrumented code.

For many non-trivial applications, it will be necessary to do some manual instrumentation to get a more cohesive picture of what is going on. For example, if your application interally uses a work queue with multiple pre-started worker threads arbitrarily accepting items, there is no way for the Java agent to generically keep track of the relationship between input and output items of the queue – you must link them manually by somehow propagating the span context between the two ends and ensure the span scope is closed and reactivated when work is stopped and started in a new thread. The manually instrumented code will seamlessly interleave with the auto-instrumented code (i.e. if you start a span in a function and within that function an auto-instrumented library is used, the generated spans will be part of the same trace).

Manual Instrumentation (without the Java agent)

To instrument your Java application without the Java agent, we recommend using the Jaeger Java tracer, which is the same thing the Java agent uses. Our backend and Smart Gateway will accept Jaeger’s Thrift-encoded spans over HTTP. We have an example application that shows the use of Jaeger that provides detailed instructions and sample code.