All Products
Search
Document Center

Application Real-Time Monitoring Service:Report Java application data with Jaeger

Last Updated:Mar 11, 2026

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.

Important

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:

MethodUse caseEffort
Manual instrumentationFull control over span creation and context propagationHighest -- you write all tracing code
Spring Cloud instrumentationSpring-based applicationsLow -- auto-instruments most Spring components
gRPC instrumentationgRPC servicesLow -- interceptors handle tracing automatically

Prerequisites

Get a reporting endpoint before you start:

  1. Log on to the ARMS console. In the left-side navigation pane, click Integration Center.

  2. On the Integration Center page, click the Jaeger card in the Server-side Applications section.

  3. In the Jaeger panel, click the Start Integration tab, then select the region where you want to report data.

    Note

    Resources are automatically initialized in a region the first time you access it.

  4. Configure Connection Type and Export Protocol, then copy the endpoint.

    ParameterRecommended valueDescription
    Connection TypeAlibaba Cloud VPC NetworkSelect this if your service runs on Alibaba Cloud in the selected region. Otherwise, select Public Network.
    Export ProtocolgRPCSelect based on the protocol supported by your client.

    Endpoint configuration

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.

    Direct reporting without Jaeger agent

  • Agent-based reporting -- The application sends trace data through a local Jaeger agent, which forwards it to the endpoint.

    Reporting through Jaeger agent

Supported Java frameworks

The OpenTracing community provides instrumentation components for the following frameworks:

FrameworkRepository
Apache HttpClientjava-apache-httpclient
Elasticsearchjava-elasticsearch-client
JDBCjava-jdbc
Kafkajava-kafka-client
Memcachedjava-memcached-client
Mongojava-mongo-driver
OkHttpjava-okhttp
Redisjava-redis-client
Spring Bootjava-spring-web
Spring Cloudjava-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.

Note

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.

1.jpg

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:

CategoryComponents
Async and scheduling@Async, @Scheduled, Executors
HTTP clientsFeign, HystrixFeign, RestTemplates
Circuit breakersHystrix
Data accessJDBC, Mongo, Redis
MessagingJMS, RabbitMQ, Spring Messaging (message channels)
ReactiveRxJava
WebSpring Web (RestControllers, WebAsyncTask), WebSocket STOMP, Zuul
LoggingStandard Logging (adds logs to the current span)
Note

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.

Note

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.