Skip to main content

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).

VariableDescriptionValue
OTEL_SERVICE_NAMEName in the dashboardorder-service
OTEL_EXPORTER_OTLP_ENDPOINTCollector addresshttp://otel-collector:4317
OTEL_METRICS_EXPORTERMetrics protocolotlp
OTEL_LOGS_EXPORTERLogs protocolotlp
OTEL_RESOURCE_ATTRIBUTESMetadataenv=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.

  1. Check App Logs: Look for [otel.javaagent] initialization info.
  2. Collector Logs: If you enabled the debug or logging exporter in the collector, you will see spans appearing in the collector’s console.
  3. 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 @WithSpan annotation:
import io.opentelemetry.instrumentation.annotations.WithSpan;

@WithSpan("process_payment")
public void process() { ... }
  • High Overhead: If CPU spikes, increase the batch processor timeout in the collector config to reduce the frequency of network calls.