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.

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:

  1. Download the OpenTelemetry Java Agent JAR file
  2. Attach the agent to your JVM using the -javaagent flag
  3. Configure the agent using environment variables or system properties
  4. The agent automatically instruments loaded classes at runtime
  5. 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:

shell
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

Or download a specific version:

shell
# 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:

shell
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

Step 4: Run Your Application

Enable the agent by providing the -javaagent flag when starting your application:

shell
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar

For Spring Boot applications:

shell
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:

Basic trace

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:

shell
# 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):

shell
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

Configuration File

For complex setups, use a properties file:

properties
# 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:

shell
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:

shell
# 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

dockerfile
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

yaml
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:

yaml
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:

yaml
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:

  1. Enable debug logging to see agent initialization:
shell
export OTEL_JAVAAGENT_DEBUG=true
java -javaagent:opentelemetry-javaagent.jar -jar myapp.jar
  1. Verify the agent is attached:
shell
jps -v | grep opentelemetry-javaagent
  1. Check agent logs in the console for initialization messages:
text
[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:

  1. Use recent Java versions (17+) which have faster class loading
  2. Disable unnecessary instrumentations:
shell
export OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=false
export OTEL_INSTRUMENTATION_SPRING_WEBMVC_ENABLED=true
export OTEL_INSTRUMENTATION_JDBC_ENABLED=true
  1. Use Application Class-Data Sharing (AppCDS) for faster startup:
shell
# 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:

  1. Reduce batch size and queue size:
shell
export OTEL_BSP_MAX_QUEUE_SIZE=1024
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=256
  1. Enable sampling for high-traffic applications:
shell
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1  # Sample only 10%
  1. Increase heap size if needed:
shell
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):

shell
export OTEL_INSTRUMENTATION_JDBC_STATEMENT_SANITIZER_ENABLED=false

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:

shell
# 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:

shell
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:

java
// 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:

java
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

AspectJava Agent (Zero-Code)Spring Boot Starter
Setup Time5 minutes15-30 minutes
Code ChangesNoneMinimal (dependencies + config)
Auto-Instrumentation150+ librariesLimited frameworks
Startup Overhead100-500ms50-100ms
Memory Overhead50-150MB20-40MB
ConfigurationEnvironment variablesapplication.yml
Native Image Support❌ Not supported✅ Supported (GraalVM)
Best ForQuick setup, existing appsSpring 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

See Also