Instrument a Java application with Jaeger to report trace data to Managed Service for OpenTelemetry. Once connected, the ARMS console displays application topology, traces, abnormal transactions, slow transactions, and SQL analysis for your application.
For new integrations, use OpenTelemetry Protocol (OTLP) instead. OTLP provides richer features, advanced tracing capabilities, and a better overall experience. The Jaeger approach described below is for teams already using the Jaeger client library.
Three instrumentation methods are available:
| Method | Use case | Effort |
|---|---|---|
| Manual instrumentation | Full control over span creation and context propagation | Highest -- you write all tracing code |
| Spring Cloud instrumentation | Spring-based applications | Low -- auto-instruments most Spring components |
| gRPC instrumentation | gRPC services | Low -- interceptors handle tracing automatically |
Prerequisites
Get a reporting endpoint before you start:
Log on to the ARMS console. In the left-side navigation pane, click Integration Center.
On the Integration Center page, click the Jaeger card in the Server-side Applications section.
In the Jaeger panel, click the Start Integration tab, then select the region where you want to report data.
NoteResources are automatically initialized in a region the first time you access it.
Configure Connection Type and Export Protocol, then copy the endpoint.
Parameter Recommended value Description Connection Type Alibaba Cloud VPC Network Select this if your service runs on Alibaba Cloud in the selected region. Otherwise, select Public Network. Export Protocol gRPC Select based on the protocol supported by your client. 
How data reporting works
Jaeger is an open source distributed tracing system. It is compatible with the OpenTracing API and has joined the Cloud Native Computing Foundation (CNCF). Jaeger gathers real-time monitoring data from various heterogeneous systems.
Jaeger supports two reporting architectures:
Direct reporting -- The application sends trace data directly to the Managed Service for OpenTelemetry endpoint, without a Jaeger agent.
Agent-based reporting -- The application sends trace data through a local Jaeger agent, which forwards it to the endpoint.
Supported Java frameworks
The OpenTracing community provides instrumentation components for the following frameworks:
| Framework | Repository |
|---|---|
| Apache HttpClient | java-apache-httpclient |
| Elasticsearch | java-elasticsearch-client |
| JDBC | java-jdbc |
| Kafka | java-kafka-client |
| Memcached | java-memcached-client |
| Mongo | java-mongo-driver |
| OkHttp | java-okhttp |
| Redis | java-redis-client |
| Spring Boot | java-spring-web |
| Spring Cloud | java-spring-cloud |
Instrument a Java application
Manual instrumentation gives you full control over span creation and context propagation. The following steps cover adding the dependency, initializing a tracer, creating spans, and propagating trace context across services.
Download the demo project, navigate to the manualDemo directory, and follow the instructions in README.md to run the program.
Step 1: Add the Jaeger client dependency
Add the following dependency to your pom.xml file:
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>0.31.0</version>
</dependency>Step 2: Initialize the tracer
Create and register a global tracer. The tracer handles span creation, context propagation (via Extract and Inject), and specifying the current span. It also contains data such as the endpoint used for data reporting, local IP address, sampling rate, and service name. You can adjust the sampling rate to reduce the overheads caused by data reporting.
Replace <endpoint> with the endpoint you copied in the Prerequisites section.
// Replace manualDemo with your application name.
io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("manualDemo");
io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
// Replace <endpoint> with the endpoint you copied in Prerequisites.
sender.withEndpoint("<endpoint>");
config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
// Set resource tags, such as the application version and deployment environment.
Map<String, String> map = new HashMap<>();
map.put("service.version", "1.0.0");
map.put("deployment.environment", "test");
config.withTracerTags(map);
GlobalTracer.register(config.getTracer());Step 3: Create spans to record operations
Build a span to capture the timing and metadata of an operation:
Tracer tracer = GlobalTracer.get();
// Create a span.
Span span = tracer.buildSpan("parentSpan").withTag("myTag", "spanFirst").start();
tracer.scopeManager().activate(span, false);
tracer.activeSpan().setTag("methodName", "testTracing");
// The business logic.
secondBiz();
span.finish();Step 4 (optional): Create child spans
To trace sub-operations within a request, create child spans linked to a parent:
Tracer tracer = GlobalTracer.get();
Span parentspan = tracer.activeSpan();
Tracer.SpanBuilder spanBuilder = tracer.buildSpan("childSpan").withTag("myTag", "spanSecond");
if (parentspan !=null) {
spanBuilder.asChildOf(parentspan).start();
}
Span childSpan = spanBuilder.start();
Scope scope = tracer.scopeManager().activate(childSpan); // Activate when the request starts.
// The business logic. The buildSpan function can be called multiple times.
childSpan.finish();
tracer.activeSpan().setTag("methodName", "testCall");
// Close the scope when the request ends.
scope.close();Step 5 (optional): Add custom tags
Add custom tags to spans for richer debugging context, such as error flags or return values:
tracer.activeSpan().setTag("methodName", "testCall");Step 6: Propagate context across services
In distributed systems, trace context (TraceId, ParentSpanId, SpanId, and Sampled) must travel with each remote procedure call (RPC). Use the Inject and Extract methods to embed trace data in HTTP headers.

Inject context on the client:
private void attachTraceInfo(Tracer tracer, Span span, final Request request) {
tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new TextMap() {
@Override
public void put(String key, String value) {
request.setHeader(key, value);
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
throw new UnsupportedOperationException("TextMapInjectAdapter should only be used with Tracer.inject()");
}
});
}Extract context on the server:
protected Span extractTraceInfo(Request request, Tracer tracer) {
Tracer.SpanBuilder spanBuilder = tracer.buildSpan("/api/xtrace/test03");
try {
SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapExtractAdapter(request.getAttachments()));
if (spanContext !=null) {
spanBuilder.asChildOf(spanContext);
}
} catch (Exception e) {
spanBuilder.withTag("Error", "extract from request fail, error msg:" + e.getMessage());
}
return spanBuilder.start();
}Instrument with Spring Cloud
Spring Cloud instrumentation automatically creates spans for a wide range of components, requiring minimal code changes. Define a Tracer bean and Spring Cloud handles the rest.
Supported components:
| Category | Components |
|---|---|
| Async and scheduling | @Async, @Scheduled, Executors |
| HTTP clients | Feign, HystrixFeign, RestTemplates |
| Circuit breakers | Hystrix |
| Data access | JDBC, Mongo, Redis |
| Messaging | JMS, RabbitMQ, Spring Messaging (message channels) |
| Reactive | RxJava |
| Web | Spring Web (RestControllers, WebAsyncTask), WebSocket STOMP, Zuul |
| Logging | Standard Logging (adds logs to the current span) |
Download the demo project, navigate to the springMvcDemo/webmvc4-boot directory, and follow the instructions in README.md to run the program.
Step 1: Add dependencies
Add the following dependencies to your pom.xml file:
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-cloud-starter</artifactId>
<version>0.5.8</version>
</dependency>
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>1.4.0</version>
</dependency>Step 2: Register an OpenTracing Tracer bean
Define a Tracer bean in your Spring configuration. Replace <endpoint> with the endpoint you copied in the Prerequisites section.
@Bean
public io.opentracing.Tracer tracer() {
io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("springFrontend");
io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
sender.withEndpoint("<endpoint>");
config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
return config.getTracer();
}Spring Cloud auto-detects the Tracer bean and instruments all supported components.
Instrument with gRPC
gRPC instrumentation uses interceptors to trace all inbound and outbound gRPC calls automatically.
Download the demo project, navigate to the grpcDemo directory, and follow the instructions in README.md to run the program.
Step 1: Add the opentracing-grpc dependency
Add the following dependency to your pom.xml file:
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-grpc</artifactId>
<version>0.2.3</version>
</dependency>Step 2: Add a tracing interceptor to the server
Initialize the Tracer, create a ServerTracingInterceptor, and register it with the gRPC server:
import io.opentracing.Tracer;
public class YourServer {
private int port;
private Server server;
private final Tracer tracer;
private void start() throws IOException {
ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor(this.tracer);
// If GlobalTracer is used:
// ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor();
server = ServerBuilder.forPort(port)
.addService(tracingInterceptor.intercept(someServiceDef))
.build()
.start();
}
}Step 3: Add a tracing interceptor to the client
Initialize the Tracer, create a ClientTracingInterceptor, and apply it to the gRPC channel:
import io.opentracing.Tracer;
public class YourClient {
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
private final Tracer tracer;
public YourClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext(true)
.build();
ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor(this.tracer);
// If GlobalTracer is used:
// ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor();
blockingStub = GreeterGrpc.newBlockingStub(tracingInterceptor.intercept(channel));
}
}FAQ
Q: No data appears in the console after running the demo. What should I do?
A: Debug the io.jaegertracing.thrift.internal.senders.HttpSender.send(Process process, List<Span> spans) method and check the return value. If an HTTP 403 error is returned, the endpoint is invalid. Verify the endpoint you copied in the Prerequisites section and confirm you selected the correct region and connection type.