Managed Service for OpenTelemetry collects trace data from Java applications and provides application topology, traces, abnormal and slow transaction analysis, and SQL analysis. Three instrumentation approaches are available, from zero-code agent setup to full SDK control.
| Approach | Effort | When to use |
|---|---|---|
| OpenTelemetry Java agent (recommended) | Minimal -- attach a JAR, no code changes | Most applications. Start here. |
| OpenTelemetry SDK for Java | Moderate -- write instrumentation code | Custom spans, attributes, or unsupported frameworks |
| Agent + SDK combined | Moderate -- agent handles the basics, SDK adds custom spans | Automatic coverage plus targeted custom instrumentation |
Sample code
Clone or browse the sample project for a working reference:
git clone https://github.com/alibabacloud-observability/java-demo.git
cd java-demo/opentelemetry-demoMethod 1: Automatic instrumentation with the OpenTelemetry Java agent
The OpenTelemetry Java agent attaches to your JVM at startup and instruments hundreds of libraries and frameworks without any code changes. This is the recommended starting point for most applications.
Step 1: Download the agent
Download the latest agent JAR from GitHub Releases:
# wget
wget -O opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# or curl
curl -Lo opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jarStep 2: Configure JVM parameters and start the application
Add the -javaagent flag before the -jar argument. Choose either the HTTP or gRPC protocol.
HTTP
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=<your-service-name>,service.version=<your-version>,deployment.environment=<your-env> \
-Dotel.exporter.otlp.protocol=http/protobuf \
-Dotel.exporter.otlp.traces.endpoint=<traces-endpoint> \
-Dotel.exporter.otlp.metrics.endpoint=<metrics-endpoint> \
-Dotel.logs.exporter=none \
-jar /path/to/your/app.jarReplace the placeholders with your actual values:
| Placeholder | Description | Example |
|---|---|---|
<your-service-name> | A name that identifies your application | order-service |
<your-version> | Application version | 1.0.0 |
<your-env> | Deployment environment | production |
<traces-endpoint> | Trace endpoint from the Prerequisites section | http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/traces |
<metrics-endpoint> | Metric endpoint from the Prerequisites section | http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/metrics |
Example:
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=order-service,service.version=1.0.0,deployment.environment=production \
-Dotel.exporter.otlp.protocol=http/protobuf \
-Dotel.exporter.otlp.traces.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/traces \
-Dotel.exporter.otlp.metrics.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/metrics \
-Dotel.logs.exporter=none \
-jar /path/to/your/app.jargRPC
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=<your-service-name>,service.version=<your-version>,deployment.environment=<your-env> \
-Dotel.exporter.otlp.protocol=grpc \
-Dotel.exporter.otlp.headers=Authentication=<token> \
-Dotel.exporter.otlp.endpoint=<endpoint> \
-Dotel.logs.exporter=none \
-jar /path/to/your/app.jarReplace the placeholders with your actual values:
| Placeholder | Description | Example |
|---|---|---|
<token> | Authentication token from the Prerequisites section | ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe**** |
<endpoint> | gRPC endpoint from the Prerequisites section | http://tracing-analysis-dc-hz-internal.aliyuncs.com:8090 |
Example:
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=order-service,service.version=1.0.0,deployment.environment=production \
-Dotel.exporter.otlp.protocol=grpc \
-Dotel.exporter.otlp.headers=Authentication=ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe**** \
-Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com:8090 \
-Dotel.logs.exporter=none \
-jar /path/to/your/app.jarTo forward trace data through an OpenTelemetry Collector, remove-Dotel.exporter.otlp.headers=Authentication=<token>and set<endpoint>to the Collector address on your on-premises machine.
Step 3: Verify trace data
On the Applications page, click your application name.
Confirm that traces appear on the application details page.
Troubleshooting:
If no data appears, verify that the endpoint and token are correct.
Enable debug logging to inspect the agent behavior:
-Dotel.javaagent.debug=trueTemporarily disable the agent without removing it from the startup command:
-Dotel.javaagent.enabled=false
Method 2: Manual instrumentation with OpenTelemetry SDK for Java
Use OpenTelemetry SDK for Java when you need full control over which operations produce spans, what attributes they carry, or when the automatic agent does not cover your framework.
Step 1: Add Maven dependencies
Add the following to your pom.xml:
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>1.30.0-alpha</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.30.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Step 2: Initialize the tracer
Create a helper class that configures the exporter, resource attributes, and tracer. Select the protocol that matches your environment.
HTTP
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
public class OpenTelemetrySupport {
static {
// Define the resource that describes this service
Resource resource = Resource.getDefault()
.merge(Resource.create(Attributes.of(
ResourceAttributes.SERVICE_NAME, "<your-service-name>",
ResourceAttributes.SERVICE_VERSION, "<your-version>",
ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "<your-env>",
ResourceAttributes.HOST_NAME, "<your-host-name>"
)));
// Build a tracer provider with the OTLP HTTP exporter
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(OtlpHttpSpanExporter.builder()
.setEndpoint("<endpoint>") // Trace endpoint from the Prerequisites section
.build()).build())
.setResource(resource)
.build();
// Register the SDK globally
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
.buildAndRegisterGlobal();
tracer = openTelemetry.getTracer("OpenTelemetry Tracer", "1.0.0");
}
private static Tracer tracer;
public static Tracer getTracer() {
return tracer;
}
}gRPC
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
public class OpenTelemetrySupport {
static {
// Define the resource that describes this service
Resource resource = Resource.getDefault()
.merge(Resource.create(Attributes.of(
ResourceAttributes.SERVICE_NAME, "<your-service-name>",
ResourceAttributes.SERVICE_VERSION, "<your-version>",
ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "<your-env>",
ResourceAttributes.HOST_NAME, "<your-host-name>"
)));
// Build a tracer provider with the OTLP gRPC exporter
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
.setEndpoint("<endpoint>") // gRPC endpoint from the Prerequisites section
.addHeader("Authentication", "<token>") // Authentication token from the Prerequisites section
.build()).build())
.setResource(resource)
.build();
// Register the SDK globally
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
.buildAndRegisterGlobal();
tracer = openTelemetry.getTracer("OpenTelemetry Tracer", "1.0.0");
}
private static Tracer tracer;
public static Tracer getTracer() {
return tracer;
}
}Step 3: Create spans
Use the tracer to create parent and child spans. Each span captures a unit of work, along with any attributes and error status.
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Scope;
public class Main {
public static void parentMethod() {
// Start a parent span
Span span = OpenTelemetrySupport.getTracer().spanBuilder("parent span").startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("good", "job");
childMethod();
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, "handle parent span error");
} finally {
span.end();
}
}
public static void childMethod() {
// Start a child span -- automatically linked to the parent through Context
Span span = OpenTelemetrySupport.getTracer().spanBuilder("child span").startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("hello", "world");
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, "handle child span error");
} finally {
span.end();
}
}
public static void main(String[] args) {
parentMethod();
}
}Step 4: Start the application and verify
Start the application, then open the Managed Service for OpenTelemetry console. On the Applications page, click your application name and confirm that traces appear.
Method 3: Combine the Java agent with the SDK
Use the agent for broad automatic coverage and the SDK for targeted custom spans. The agent auto-configures the SDK at startup through the opentelemetry-sdk-extension-autoconfigure dependency, so you do not need the OpenTelemetrySupport helper class from Method 2.
Step 1: Download the agent
Download the OpenTelemetry Java agent as described in Method 1, Step 1.
Step 2: Add Maven dependencies
In addition to the dependencies in Method 2, Step 1, add the following:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-annotations</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
<version>1.23.0-alpha</version>
</dependency>The opentelemetry-sdk-extension-autoconfigure dependency transfers agent settings to the SDK automatically, so you do not need to configure the exporter or resource attributes in code.Step 3: Get a tracer
Because the agent handles SDK initialization, get the global tracer directly:
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
Tracer tracer = openTelemetry.getTracer("instrumentation-library-name", "1.0.0");Step 4: Add custom instrumentation
Three techniques for adding instrumentation on top of the agent:
Technique 1: Add attributes to an auto-created span
Call Span.current() inside an already-instrumented method to attach business attributes:
@RequestMapping("/async")
public String async() {
Span span = Span.current();
span.setAttribute("user.id", "123456");
userService.async();
child("vip");
return "async";
}Technique 2: Use @WithSpan for annotation-based instrumentation
Annotate a method with @WithSpan to create a span automatically. Use @SpanAttribute to record parameters:
@WithSpan
private void child(@SpanAttribute("user.type") String userType) {
System.out.println(userType);
biz();
}Technique 3: Create spans manually with a tracer
For full control, build spans with the tracer API. This example also propagates context into an async thread:
private void biz() {
Tracer tracer = GlobalOpenTelemetry.get().getTracer("tracer");
Span span = tracer.spanBuilder("biz (manual)")
.setParent(Context.current().with(Span.current())) // optional -- set automatically
.startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("biz-id", "111");
// Propagate context into an async task
es.submit(() -> {
Span asyncSpan = tracer.spanBuilder("async")
.setParent(Context.current().with(span))
.startSpan();
try {
Thread.sleep(1000L); // simulate async work
} catch (Throwable e) {
// handle error
}
asyncSpan.end();
});
Thread.sleep(1000); // simulate business logic
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, "handle biz error");
} finally {
span.end();
}
}Full controller and service code
Controller (com.alibaba.arms.brightroar.console.controller):
package com.alibaba.arms.brightroar.console.controller;
import com.alibaba.arms.brightroar.console.service.UserService;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.extension.annotations.SpanAttribute;
import io.opentelemetry.extension.annotations.WithSpan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
private ExecutorService es = Executors.newFixedThreadPool(5);
// Technique 1: Add attributes to an auto-created span
@RequestMapping("/async")
public String async() {
System.out.println("UserController.async -- " + Thread.currentThread().getId());
Span span = Span.current();
span.setAttribute("user.id", "123456");
userService.async();
child("vip");
return "async";
}
// Technique 2: Annotation-based instrumentation
@WithSpan
private void child(@SpanAttribute("user.type") String userType) {
System.out.println(userType);
biz();
}
// Technique 3: Manual span creation
private void biz() {
Tracer tracer = GlobalOpenTelemetry.get().getTracer("tracer");
Span span = tracer.spanBuilder("biz (manual)")
.setParent(Context.current().with(Span.current()))
.startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("biz-id", "111");
es.submit(new Runnable() {
@Override
public void run() {
Span asyncSpan = tracer.spanBuilder("async")
.setParent(Context.current().with(span))
.startSpan();
try {
Thread.sleep(1000L); // simulate async work
} catch (Throwable e) {
}
asyncSpan.end();
}
});
Thread.sleep(1000); // simulate business logic
System.out.println("biz done");
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
openTelemetry.getPropagators();
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, "handle biz error");
} finally {
span.end();
}
}
}Service (com.alibaba.arms.brightroar.console.service):
package com.alibaba.arms.brightroar.console.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Async
public void async() {
System.out.println("UserService.async -- " + Thread.currentThread().getId());
System.out.println("my name is async");
System.out.println("UserService.async -- ");
}
}Step 5: Configure JVM parameters and start the application
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=<your-service-name> \
-Dotel.exporter.otlp.headers=Authentication=<token> \
-Dotel.exporter.otlp.endpoint=<endpoint> \
-jar /path/to/your/app.jarExample:
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.resource.attributes=service.name=ot-java-agent-sample \
-Dotel.exporter.otlp.headers=Authentication=b590xxxxuqs@3a75d95xxxxx9b_b59xxxxguqs@53dxxxx2afe8301 \
-Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-bj:8090 \
-jar /path/to/your/app.jarTo forward trace data through an OpenTelemetry Collector, remove-Dotel.exporter.otlp.headers=Authentication=<token>and set<endpoint>to the Collector address on your on-premises machine.
Open the Managed Service for OpenTelemetry console. On the Applications page, click your application name and confirm that traces appear.
Supported Java frameworks
The OpenTelemetry Java agent automatically instruments the following frameworks. For the complete and up-to-date list, see Supported libraries, frameworks, application servers, and JVMs.