This guide covers Spring Boot applications running on ECS, EC2, Kubernetes, or any container/VM environment. For Java on AWS Lambda, see the Java Lambda guide.

Introduction

You can instrument Spring Boot applications with OpenTelemetry using one of two approaches:
  1. OpenTelemetry Java Agent — uses bytecode instrumentation and provides the broadest out-of-the-box coverage, but has limitations around configuration and integration with other agents.
  2. OpenTelemetry Spring Boot Starter — A lightweight, flexible alternative to the Java Agent. Choose this if any of the following apply:
    • You are using Spring Boot Native Image, which the Java agent does not support
    • The Java agent’s startup overhead exceeds your requirements
    • You are already using another Java monitoring agent, which may conflict with the Otel Java agent
    The Spring Boot Starter has less out-of-the-box instrumentation than the Java Agent. However, you can easily close this gap by adding standalone instrumentation libraries for the specific libraries/frameworks you want to instrument — see the supported libraries list.
The rest of this guide covers instrumentation using the OpenTelemetry Spring Boot Starter. For a deeper dive into how it works, see this Spring blog post.

1. Add the OpenTelemetry Spring Boot Starter

Add the following dependencies to your project:
dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
    
    implementation 'org.springframework.boot:spring-boot-starter-opentelemetry'
    implementation 'org.springframework.boot:spring-boot-starter-micrometer-metrics'
    implementation('io.opentelemetry:opentelemetry-api')
    implementation "io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:${opentelemetryVersion}"
}

2. Set Application Properties

Add these properties to your application.properties file. Replace YOUR_TRACE0_ENV_API_KEY with the API key from your Trace0 environment.
management.opentelemetry.tracing.export.otlp.endpoint=https://app.trace0hq.com/api/v1/traces
management.otlp.metrics.export.url=https://app.trace0hq.com/api/v1/metrics
management.opentelemetry.logging.export.otlp.endpoint=https://app.trace0hq.com/api/v1/logs

management.opentelemetry.tracing.export.otlp.headers.X-API-KEY=YOUR_TRACE0_ENV_API_KEY
management.otlp.metrics.export.headers.X-API-KEY=YOUR_TRACE0_ENV_API_KEY
management.opentelemetry.logging.export.otlp.headers.X-API-KEY=YOUR_TRACE0_ENV_API_KEY

3. Add Standalone Instrumentation Libraries

As discussed above, the Spring Boot Starter does not instrument most libraries out of the box. To instrument additional libraries and frameworks, you need to add the corresponding OpenTelemetry instrumentation libraries to your application. This ensures you receive spans from those libraries and gain full visibility into what is happening inside your application. The example below shows how to add instrumentation for the AWS SDK.
dependencies {
    implementation "io.opentelemetry.instrumentation:opentelemetry-aws-sdk-2.2:${opentelemetryVersion}"
}
See here for the full list of OpenTelemetry instrumentation libraries. The example service below includes the AWS SDK instrumentation library and registers the DynamoDB client for instrumentation.

4. Add a Global Exception Handler

By default, unhandled exceptions in Spring Boot are caught by Spring’s error handling mechanism after the OpenTelemetry trace context has closed. This means the error is never attached to the trace, making it invisible in the Trace0 trace. Adding a @RestControllerAdvice global exception handler ensures the exception is caught and logged within the active request trace, so the error and stack trace are correctly correlated to the right trace.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handle(Exception ex) {
        log.error("Unhandled exception", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal error");
    }
}

5. Deploy Service

Deploy your service and watch traces, metrics, and logs appear in the Trace0 dashboard within seconds.

Example Service

See our Spring Boot example app for a working service you can deploy to your AWS account.