Java Zero-Code Instrumentation with Uptrace
This guide explains how to automatically instrument Java applications without modifying code using the OpenTelemetry Java Agent. The agent uses bytecode manipulation to automatically detect and instrument popular Java frameworks and libraries at runtime, exporting telemetry data to Uptrace using OTLP.
Note: The examples below use Uptrace as the OTLP backend, but you can use any OpenTelemetry-compatible backend (Jaeger, Grafana Tempo, Prometheus, etc.) by changing the OTEL_EXPORTER_OTLP_ENDPOINT and removing Uptrace-specific headers.
Quick Start: If you're already familiar with OpenTelemetry Java Agent basics, see the Quick Start Guide for basic setup. This page provides comprehensive zero-code instrumentation details, configuration options, and production deployment patterns.
What is Zero-Code Instrumentation?
Zero-code instrumentation (also called automatic instrumentation) allows you to collect telemetry data from Java applications without modifying application code. The OpenTelemetry Java Agent uses bytecode manipulation to intercept method calls in popular frameworks and libraries at class-loading time, automatically generating distributed traces, metrics, and logs.
How it works:
- Download the OpenTelemetry Java Agent JAR file
- Attach the agent to your JVM using the
-javaagentflag - Configure the agent using environment variables or system properties
- The agent automatically instruments loaded classes at runtime
- Telemetry data is automatically collected and exported to Uptrace
Prerequisites
Before starting, ensure you have:
- Java 8 or higher (Java 17+ recommended for best performance)
- An existing Java application (Spring Boot, Micronaut, Quarkus, etc.)
- An Uptrace account with a DSN
Quick Start Guide
Follow these steps to get your first auto-instrumented trace running in 5 minutes.
Step 1: Create an Uptrace Project
Create an Uptrace project to obtain a DSN (Data Source Name), for example, https://<secret>@api.uptrace.dev?grpc=4317.
Step 2: Download Java Agent
Download the latest pre-compiled Java agent JAR:
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
Or download a specific version:
# Version 2.23.0 (current stable as of January 2026)
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.23.0/opentelemetry-javaagent.jar
Step 3: Configure Environment Variables
Configure the agent to export data to Uptrace. Replace <FIXME> with your actual Uptrace DSN, and myservice with a name that identifies your application:
export OTEL_RESOURCE_ATTRIBUTES=service.name=myservice,service.version=1.0.0
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<FIXME>
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
Note: OpenTelemetry Java Agent defaults to gRPC protocol (port 4317). For HTTP/Protobuf, use port 4318 and set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf.
Step 4: Run Your Application
Enable the agent by providing the -javaagent flag when starting your application:
java -javaagent:path/to/opentelemetry-javaagent.jar \
-jar myapp.jar
For Spring Boot applications:
java -javaagent:./opentelemetry-javaagent.jar \
-jar target/my-spring-boot-app-0.0.1-SNAPSHOT.jar
Step 5: View Your Trace
Navigate to the Uptrace UI to view your traces. You should see automatic instrumentation of HTTP requests, database queries, and framework operations:

Auto-Instrumented Libraries
The OpenTelemetry Java Agent automatically instruments over 150+ popular Java libraries and frameworks. Here are the most commonly used:
Web Frameworks
- Spring Web MVC / Spring WebFlux - automatic request/response tracing
- Spring Boot - comprehensive Spring ecosystem support
- Quarkus - reactive and imperative endpoints
- Micronaut - controller and client instrumentation
- Servlet API (3.0+) - generic servlet container support
- JAX-RS - RESTful web services
- Vert.x - event-driven applications
HTTP Clients
- Apache HttpClient (4.x, 5.x)
- OkHttp (3.x, 4.x)
- Netty HTTP client
- Java 11+ HttpClient
- Spring WebClient / RestTemplate
- Reactor Netty
Database Clients
- JDBC (all JDBC-compliant drivers)
- Hibernate / JPA
- Spring Data JPA
- PostgreSQL (via JDBC)
- MySQL (via JDBC)
- MongoDB Java Driver
- Cassandra Driver
- Redis (Jedis, Lettuce)
- Elasticsearch Java client
Message Queues
- Kafka (kafka-clients)
- RabbitMQ (amqp-client)
- JMS (Java Message Service)
- AWS SQS
- Google Pub/Sub
- Azure Service Bus
Application Servers
- Tomcat
- Jetty
- Undertow
- WebLogic
- JBoss / WildFly
- GlassFish
gRPC & GraphQL
- gRPC Java (client and server)
- GraphQL Java
Logging Frameworks
For the complete list of 150+ supported libraries, see the OpenTelemetry Java Agent Supported Libraries documentation.
Configuration Options
Environment Variables
Common configuration options for zero-code instrumentation:
# Service identification
export OTEL_RESOURCE_ATTRIBUTES=service.name=my-java-app,service.version=1.0.0,deployment.environment=production
# Exporter configuration
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<your_dsn>
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
# Sampling configuration
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1 # Sample 10% of traces
# Batch span processor configuration
export OTEL_BSP_SCHEDULE_DELAY=5000
export OTEL_BSP_MAX_QUEUE_SIZE=2048
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512
# Propagators
export OTEL_PROPAGATORS=tracecontext,baggage
# Metrics configuration (for Uptrace compatibility)
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA
export OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM
For the full list of configuration options, see the OpenTelemetry Java Agent Configuration documentation.
System Properties Alternative
You can also configure the agent using Java system properties (-D flags):
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=my-java-app \
-Dotel.traces.exporter=otlp \
-Dotel.exporter.otlp.endpoint=https://api.uptrace.dev:4317 \
-Dotel.exporter.otlp.headers=uptrace-dsn=<your_dsn> \
-jar myapp.jar
Priority: System properties (-D flags) take precedence over environment variables, which take precedence over configuration files.
Configuration File
For complex setups, use a properties file:
# otel-config.properties
otel.service.name=my-java-app
otel.service.version=1.0.0
otel.resource.attributes=deployment.environment=production,service.namespace=backend
otel.traces.exporter=otlp
otel.metrics.exporter=otlp
otel.logs.exporter=otlp
otel.exporter.otlp.endpoint=https://api.uptrace.dev:4317
otel.exporter.otlp.headers=uptrace-dsn=<your_dsn>
otel.exporter.otlp.protocol=grpc
otel.exporter.otlp.compression=gzip
# Disable specific instrumentations
otel.instrumentation.spring-webmvc.enabled=true
otel.instrumentation.jdbc.enabled=true
otel.instrumentation.logback-appender.enabled=false
Load the configuration:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.javaagent.configuration-file=otel-config.properties \
-jar myapp.jar
Disabling Specific Instrumentations
Disable instrumentation for specific libraries to reduce overhead:
# Disable all instrumentations except specific ones
export OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=false
export OTEL_INSTRUMENTATION_SPRING_WEBMVC_ENABLED=true
export OTEL_INSTRUMENTATION_JDBC_ENABLED=true
export OTEL_INSTRUMENTATION_KAFKA_ENABLED=true
# Or disable specific instrumentations
export OTEL_INSTRUMENTATION_LOGBACK_APPENDER_ENABLED=false
export OTEL_INSTRUMENTATION_SPRING_SCHEDULING_ENABLED=false
Production Deployment
Docker Example
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Download OpenTelemetry Java Agent
ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar /app/opentelemetry-javaagent.jar
# Copy application JAR
COPY target/myapp.jar /app/app.jar
# Configure OpenTelemetry
ENV OTEL_RESOURCE_ATTRIBUTES=service.name=my-java-app,service.version=1.0.0
ENV OTEL_TRACES_EXPORTER=otlp
ENV OTEL_METRICS_EXPORTER=otlp
ENV OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
ENV OTEL_EXPORTER_OTLP_PROTOCOL=grpc
# Run with agent
CMD ["java", "-javaagent:/app/opentelemetry-javaagent.jar", "-jar", "/app/app.jar"]
Docker Compose Example
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- OTEL_RESOURCE_ATTRIBUTES=service.name=spring-boot-app,service.version=1.0.0
- OTEL_TRACES_EXPORTER=otlp
- OTEL_METRICS_EXPORTER=otlp
- OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
- OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=${UPTRACE_DSN}
- OTEL_TRACES_SAMPLER=parentbased_traceidratio
- OTEL_TRACES_SAMPLER_ARG=0.1
Kubernetes Deployment
For Kubernetes, use the OpenTelemetry Operator to inject auto-instrumentation automatically:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: java-instrumentation
namespace: default
spec:
exporter:
endpoint: https://api.uptrace.dev:4317
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "0.1"
java:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
env:
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "uptrace-dsn=<your_dsn>"
Apply instrumentation to your deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: "true"
spec:
containers:
- name: app
image: my-java-app:latest
env:
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.name=java-app,service.version=1.0.0"
For more details, see the Kubernetes monitoring guide.
Troubleshooting
Issue: Agent Not Loading
Symptom: Application starts but no traces appear in Uptrace.
Solution:
- Enable debug logging to see agent initialization:
export OTEL_JAVAAGENT_DEBUG=true
java -javaagent:opentelemetry-javaagent.jar -jar myapp.jar
- Verify the agent is attached:
jps -v | grep opentelemetry-javaagent
- Check agent logs in the console for initialization messages:
[otel.javaagent] OpenTelemetry Javaagent 2.10.0 started
[otel.javaagent] Using OTLP endpoint: https://api.uptrace.dev:4317
Issue: High Startup Time
Symptom: Application startup time increased significantly (>10 seconds).
Solution:
The agent performs bytecode manipulation at class-loading time, which adds overhead. Optimize startup:
- Use recent Java versions (17+) which have faster class loading
- Disable unnecessary instrumentations:
export OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=false
export OTEL_INSTRUMENTATION_SPRING_WEBMVC_ENABLED=true
export OTEL_INSTRUMENTATION_JDBC_ENABLED=true
- Use Application Class-Data Sharing (AppCDS) for faster startup:
# Create AppCDS archive
java -javaagent:opentelemetry-javaagent.jar -XX:DumpLoadedClassList=classes.lst -jar myapp.jar
java -javaagent:opentelemetry-javaagent.jar -Xshare:dump -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=app-cds.jsa -jar myapp.jar
# Run with AppCDS
java -javaagent:opentelemetry-javaagent.jar -Xshare:on -XX:SharedArchiveFile=app-cds.jsa -jar myapp.jar
Issue: High Memory Usage
Symptom: JVM heap usage increased by 100+ MB after enabling agent.
Solution:
The agent maintains span buffers and metadata. Optimize memory usage:
- Reduce batch size and queue size:
export OTEL_BSP_MAX_QUEUE_SIZE=1024
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=256
- Enable sampling for high-traffic applications:
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1 # Sample only 10%
- Increase heap size if needed:
java -Xmx2g -javaagent:opentelemetry-javaagent.jar -jar myapp.jar
Issue: Missing SQL Query Details
Symptom: Database spans appear but without SQL statements.
Solution:
By default, SQL statements are sanitized. Enable detailed query capture (only in non-production):
export OTEL_INSTRUMENTATION_JDBC_STATEMENT_SANITIZER_ENABLED=false
Security Warning: Disabling sanitization may expose sensitive data in traces. Only use in development environments.
Issue: Agent Conflicts with Other Agents
Symptom: Application fails to start with multiple Java agents (APM tools).
Solution:
Most JVM profiling agents cannot coexist. Remove conflicting agents:
# Remove New Relic, AppDynamics, Datadog agents
# Keep only OpenTelemetry Java Agent
java -javaagent:opentelemetry-javaagent.jar -jar myapp.jar
If you must use multiple agents, load OpenTelemetry agent last:
java -javaagent:other-agent.jar -javaagent:opentelemetry-javaagent.jar -jar myapp.jar
Limitations of Zero-Code Instrumentation
While zero-code instrumentation provides comprehensive observability, it has some limitations:
Generic Span Names
Automatic instrumentation generates generic span names based on framework operations:
- ❌ Automatic:
GET /api/orders - ✅ Manual:
process_high_priority_customer_orders
No Business Context
Zero-code instrumentation can't capture domain-specific information like user IDs, order amounts, or custom business logic:
// Automatically traced (HTTP request)
@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
// NOT traced (business logic details)
public Order processOrder(Order order, User user) {
// Business rules invisible without manual instrumentation
if (user.isPremium()) {
applyDiscounts(order);
}
return order;
}
To add business context, combine zero-code instrumentation with manual tracing:
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
@Service
public class OrderService {
private final Tracer tracer;
public Order processOrder(Order order, User user) {
// Zero-code instruments the HTTP layer automatically
// Add manual span for business logic
Span span = tracer.spanBuilder("process_order")
.setAttribute("order.id", order.getId())
.setAttribute("user.id", user.getId())
.setAttribute("user.tier", user.getTier())
.startSpan();
try (Scope scope = span.makeCurrent()) {
Order result = applyBusinessRules(order, user);
span.setAttribute("order.total", result.getTotal());
return result;
} finally {
span.end();
}
}
}
Framework Coverage Only
Only instrumented libraries generate spans. Custom utilities and proprietary frameworks remain invisible without manual instrumentation.
Performance Overhead
Instrumenting all libraries can introduce overhead:
- Typical overhead: 2-5% CPU increase
- Memory overhead: 50-150MB additional heap usage
- Startup time: +100-500ms depending on application size
For performance-critical applications, consider selective instrumentation or disable non-essential instrumentations.
Zero-Code vs Spring Boot Starter
| Aspect | Java Agent (Zero-Code) | Spring Boot Starter |
|---|---|---|
| Setup Time | 5 minutes | 15-30 minutes |
| Code Changes | None | Minimal (dependencies + config) |
| Auto-Instrumentation | 150+ libraries | Limited frameworks |
| Startup Overhead | 100-500ms | 50-100ms |
| Memory Overhead | 50-150MB | 20-40MB |
| Configuration | Environment variables | application.yml |
| Native Image Support | ❌ Not supported | ✅ Supported (GraalVM) |
| Best For | Quick setup, existing apps | Spring Boot 3+, Native image |
For detailed Spring Boot Starter setup, see the Spring Boot OpenTelemetry guide.
Recommendation: Use Java Agent for quickest setup and maximum coverage. Use Spring Boot Starter for Native Image applications or when you need Spring-native configuration.
Next Steps
- Learn about OpenTelemetry Java Tracing API to add custom spans
- Configure OpenTelemetry Sampling for production environments
- Explore OpenTelemetry Metrics to track business KPIs
- Review Spring Boot microservices monitoring for distributed systems
- Compare with OpenTelemetry vs Micrometer for Spring metrics