Instrumentation
Java OTel - Virex Docs
Instrument Java applications using the TraceFlow OpenTelemetry agent for automatic bytecode injection.
Java Instrumentation with OpenTelemetry Collector
In a modern observability stack, your Java application shouldn’t send data directly to a backend. Instead, it should send data to a Collector. This “Gateway” pattern allows you to scrub PII, aggregate metrics, and retry failed exports without impacting application performance.
Why use a Collector?
- Offload Processing: The application spends less CPU/RAM on telemetry.
- Security: The Collector handles API keys and secrets, so they aren’t scattered across every app instance.
- Format Flexibility: Send OTLP from your app, and have the Collector translate it to Prometheus, Jaeger, or Datadog.
1. Configure the OpenTelemetry Collector
First, set up your config.yaml for the collector. This configuration opens a gRPC port to receive data from your Java app and exports it to your desired backend.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
exporters:
# Example: Sending to a logging backend or vendor
otlp/backend:
endpoint: "ingest.your-vendor.com:4317"
headers:
"x-api-key": "${API_KEY}"
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/backend]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp/backend]
2. Prepare the Java Agent
The Java agent uses bytecode manipulation to instrument popular frameworks (Spring, Kafka, JDBC) automatically.
Step 1: Download the Agent
curl -L [https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar) -o opentelemetry-javaagent.jar
Step 2: Set Environment Variables
Point your application to the Collector’s endpoint (usually port 4317).
| Variable | Description | Value |
|---|---|---|
| OTEL_SERVICE_NAME | Name in the dashboard | order-service |
| OTEL_EXPORTER_OTLP_ENDPOINT | Collector address | http://otel-collector:4317 |
| OTEL_METRICS_EXPORTER | Metrics protocol | otlp |
| OTEL_LOGS_EXPORTER | Logs protocol | otlp |
| OTEL_RESOURCE_ATTRIBUTES | Metadata | env=prod,version=1.2.0 |
3. Launch the Application
Attach the agent using the -javaagent JVM argument. Ensure this comes before the -jar command.
Standard Command Line
java -javaagent:./opentelemetry-javaagent.jar \
-jar your-app.jar
Dockerfile Implementation
COPY opentelemetry-javaagent.jar /opt/otel-agent.jar
ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/otel-agent.jar"
ENTRYPOINT ["java", "-jar", "app.jar"]
4. Verification
Once started, your application will begin a “Handshake” with the collector.
- Check App Logs: Look for
[otel.javaagent]initialization info. - Collector Logs: If you enabled the
debugorloggingexporter in the collector, you will see spans appearing in the collector’s console. - Visualization: Open your tracing backend (Jaeger/Honeycomb/Grafana) and filter by your
service.name.
Troubleshooting Common Issues
- Connection Refused: Ensure the collector is running and that the firewall allows traffic on port 4317.
- No Traces for Custom Methods: Auto-instrumentation only covers known libraries. For custom business logic, add the
@WithSpanannotation:
import io.opentelemetry.instrumentation.annotations.WithSpan;
@WithSpan("process_payment")
public void process() { ... }
- High Overhead: If CPU spikes, increase the
batchprocessor timeout in the collector config to reduce the frequency of network calls.