Spring Boot OpenTelemetry instrumentation
There are two supported ways to instrument a Spring Boot service:
- Option A — the OpenTelemetry Java agent. Attach
opentelemetry-javaagent.jarat startup. No code or build changes, and the broadest library coverage. This is the recommended path for most services. - Option B — the OpenTelemetry Spring Boot starter. Add
opentelemetry-spring-boot-starteras a dependency. Instrumentation ships inside your application artifact, configured through Spring properties. Use this when you cannot attach an agent — for example, GraalVM native images.
Pick one. Running both at once produces duplicate telemetry.
Prerequisites
Section titled “Prerequisites”- A Spring Boot application. The starter supports Spring Boot 3.x, and 2.6+ works with the same artifact.
- Your Osuite ingest endpoint (
ingest.<region>.osuite.io:443) and token (<your-ingest-token>). - Outbound network access from the application host to the ingest endpoint on port
443.
Option A: Attach the OpenTelemetry Java agent
Section titled “Option A: Attach the OpenTelemetry Java agent”The agent reads its configuration from environment variables. Pick your deployment target — every tab is a complete, self-contained path.
curl -L -o opentelemetry-javaagent.jar \ https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443"export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"export OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>"export OTEL_SERVICE_NAME="orders-service"export OTEL_RESOURCE_ATTRIBUTES="service.environment=production"
java -javaagent:./opentelemetry-javaagent.jar -jar orders-service.jarThe -javaagent flag must come before -jar.
Add the agent to the image and keep the token out of it — pass it at runtime:
FROM eclipse-temurin:21-jre
WORKDIR /appCOPY target/orders-service.jar orders-service.jarADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar opentelemetry-javaagent.jar
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443" \ OTEL_EXPORTER_OTLP_PROTOCOL="grpc" \ OTEL_SERVICE_NAME="orders-service" \ OTEL_RESOURCE_ATTRIBUTES="service.environment=production"
ENTRYPOINT ["java", "-javaagent:/app/opentelemetry-javaagent.jar", "-jar", "orders-service.jar"]docker run \ -e OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>" \ orders-service:latestBuild the image with the agent as shown in the Docker tab. Store the token in a Secret:
kubectl create secret generic osuite-ingest \ --from-literal=otlp-headers="x-osuite-ingest-token=<your-ingest-token>"Attach the agent with JAVA_TOOL_OPTIONS:
apiVersion: apps/v1kind: Deploymentmetadata: name: orders-servicespec: replicas: 1 selector: matchLabels: app: orders-service template: metadata: labels: app: orders-service spec: containers: - name: orders-service image: orders-service:latest env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/app/opentelemetry-javaagent.jar" - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "https://ingest.<region>.osuite.io:443" - name: OTEL_EXPORTER_OTLP_PROTOCOL value: "grpc" - name: OTEL_SERVICE_NAME value: "orders-service" - name: OTEL_RESOURCE_ATTRIBUTES value: "service.environment=production" - name: OTEL_EXPORTER_OTLP_HEADERS valueFrom: secretKeyRef: name: osuite-ingest key: otlp-headersOption B: Add the OpenTelemetry Spring Boot starter
Section titled “Option B: Add the OpenTelemetry Spring Boot starter”Add the starter and its BOM to your build. The BOM keeps every OpenTelemetry artifact on a compatible version.
<dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-bom</artifactId> <version>2.11.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
<dependencies> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-spring-boot-starter</artifactId> </dependency></dependencies>implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.11.0"))implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter")Configure the exporter in application.properties. The starter reads both otel.* properties and the standard OTEL_* environment variables. Keep the token in an environment variable rather than in source control:
spring.application.name=orders-serviceotel.exporter.otlp.endpoint=https://ingest.<region>.osuite.io:443otel.exporter.otlp.protocol=grpcotel.exporter.otlp.headers=x-osuite-ingest-token=${OSUITE_INGEST_TOKEN}otel.resource.attributes=service.environment=productionThe starter derives service.name from spring.application.name. Set OSUITE_INGEST_TOKEN in the runtime environment, then start the application normally with java -jar orders-service.jar.
Verify in Osuite
Section titled “Verify in Osuite”What you should see
Start the service and send it some traffic. It appears in APM within a minute, emitting request-rate, error-rate, and latency metrics, and its requests show up as traces in the trace explorer.
Troubleshooting
Section titled “Troubleshooting”For agent-attach, duplicate-span, and native-image problems, see Java troubleshooting.
No data in Osuite after a few minutes? Work through these checks.
- Endpoint — confirm the exporter targets exactly
ingest.<region>.osuite.io:443for your region, over TLS. - Token — confirm the ingest header carries a valid
<your-ingest-token>and has not been rotated. - Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the
otlp/osuiteexporter attached. - Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
- Network — confirm the host has outbound access to the ingest endpoint on port
443. - Timing — allow up to a minute for the first data to appear before assuming a failure.
Still stuck? Ask the Investigation Agent or contact support.