Step-by-Step Guide to Integrating Spring Boot with OpenTelemetry and GCP
This guide provides a step-by-step approach to integrating OpenTelemetry with Spring Boot and deploying it to Google Cloud Platform (GCP). The setup includes configuring the necessary dependencies, setting up OpenTelemetry Collector, creating a Docker environment, and deploying the application to GCP Cloud Run.
Important Notice: This article is for reference only. Please refer to the latest guide: Step-by-Step Guide to Integrating Spring Boot and OpenTelemetry with Micrometer on GCP for Distributed Tracing for up-to-date information and recommended practices.
It’s important to note that Micrometer is now the recommended framework by Spring for metrics and tracing in Spring Boot applications. The latest Spring Boot versions have integrated Micrometer as the default metrics and tracing facade, which can be easily configured to work with OpenTelemetry. For the most current approach to implementing observability in Spring Boot applications using Micrometer and OpenTelemetry, please refer to the guide linked above.
Prerequisites
Before you start, ensure you have the following installed:
- Java 21
- Docker
- Gradle
- GCP account with Cloud Run and Artifact Registry enabled
Setting Up Spring Boot with OpenTelemetry
Configure build.gradle
To start integrating OpenTelemetry with your Spring Boot application, you need to add the necessary dependencies to your build.gradle
file. This section will guide you through the configuration process.
Define Dependencies
Add the required dependencies to your build.gradle
file:
1 | dependencies { |
Here’s a brief explanation of the manually added dependencies:
- spring-boot-starter-aop: Supports Aspect-Oriented Programming (AOP), which is necessary for using Micrometer’s
@NewSpan
annotation. This annotation helps in tracing method calls. By including AOP, you enable the ability to create spans around specific methods, allowing for detailed monitoring and tracing of your application’s behavior. - micrometer-tracing-bridge-otel: Bridges Micrometer tracing to OpenTelemetry. This dependency allows for tracing using the OpenTelemetry instrumentation. This bridge facilitates the propagation of trace context and spans created by Micrometer into OpenTelemetry’s tracing system. It ensures compatibility between the two systems and enables seamless monitoring and observability.
- opentelemetry-spring-boot-starter: Provides auto-configuration for OpenTelemetry in Spring Boot applications. This starter simplifies the process of instrumenting your Spring Boot application with OpenTelemetry. The starter includes built-in instrumentation for many Spring Boot features, which helps you get started quickly with minimal configuration. Refer to the OpenTelemetry documentation for more details.
- context-propagation:latest.integration: Handles context propagation in asynchronous tasks. This is essential for maintaining trace context across asynchronous calls and tasks. This library ensures that trace and context information is correctly propagated across threads and asynchronous operations, which is crucial for accurate tracing and monitoring in distributed systems. Refer to the Micrometer Context Propagation documentation for more information.
Dependency Management
Import the OpenTelemetry instrumentation BOM for dependency management:
1 | dependencyManagement { |
Using the BOM (Bill of Materials) ensures that all OpenTelemetry dependencies are compatible with each other. The BOM helps in managing the versions of the dependencies and their transitive dependencies to ensure they are compatible. This approach simplifies dependency management and reduces conflicts.
By following these steps, you have successfully configured your build.gradle
file to integrate OpenTelemetry with your Spring Boot application. Next, we’ll look into setting up the main application class and configuring asynchronous context propagation.
Configuring Application Settings
To ensure proper integration of OpenTelemetry and effective tracing and monitoring, you need to configure your Spring Boot application settings. Add the following settings to your application.yml
or application.properties
file.
Configuration Settings
application.yml
1 | spring: |
- management.tracing.sampling.probability: Sets the sampling probability for tracing. A value of
1.0
means all requests will be traced. This setting is useful for ensuring comprehensive trace data during development and testing. - management.observations.annotations.enabled: Enables the use of annotations for observability. This setting allows you to use annotations like
@NewSpan
to define spans for tracing specific methods. For more details, refer to the Spring Boot Actuator Observability documentation.
Set Up the Main Application Class
Create the main application class OtelApplication.java
:
1 | import org.springframework.boot.SpringApplication; |
- @EnableAsync: This annotation enables Spring’s asynchronous method execution capability. When applied, it allows methods annotated with
@Async
to run in a separate thread, thus enabling concurrent execution. This is particularly useful for tasks that can be performed in parallel, improving the application’s responsiveness and performance.
By including @EnableAsync
, you enable asynchronous processing within your application, which is critical for handling background tasks without blocking the main execution thread. This is especially useful in the context of tracing and monitoring, where you may want to offload certain tasks to be processed asynchronously to avoid impacting the main application flow.
Configure Asynchronous Context Propagation
Create the AsyncTraceContextConfiguration.java
to handle context propagation for asynchronous tasks:
1 | package com.example.otel; |
This class configures context propagation for asynchronous tasks in a Spring Boot application. It ensures that trace information and other context data are carried over when tasks are executed asynchronously. Here’s a brief breakdown:
- Task Executor Injection: Injects a
ThreadPoolTaskExecutor
to manage thread pools for asynchronous tasks. - ContextSnapshotFactory: Creates snapshots of the current context, including trace data.
- ContextExecutorService.wrap: Wraps the
ThreadPoolTaskExecutor
to capture and propagate context information across asynchronous boundaries.
Purpose and Benefits
- Asynchronous Execution: Enables handling of asynchronous method execution by providing a custom
Executor
. - Context Propagation: Ensures that context information, such as trace data, is propagated across asynchronous tasks, maintaining the integrity of trace data for distributed tracing and monitoring.
By following this configuration, you enable asynchronous context propagation in your Spring Boot application, ensuring that trace and context information is correctly managed and propagated. This is essential for accurate and comprehensive distributed tracing and monitoring.
Create a Sample Controller and Service
This section provides sample code for a controller and a service to demonstrate how to use tracing annotations and asynchronous execution in a Spring Boot application with OpenTelemetry.
Purpose and Design of the Sample Code
The sample code includes OrderController
and OrderService
classes. These classes are designed to illustrate how to:
- Use the
@NewSpan
annotation to create new tracing spans. - Handle asynchronous execution using the
@Async
annotation. - Simulate processing work to show how tracing captures the execution flow.
OrderController.java
This class defines a REST controller for handling order-related requests.
1 | package com.example.otel; |
- @NewSpan: Annotates methods to create new tracing spans, capturing the execution of these methods as separate trace segments.
OrderService.java
This class defines the service responsible for order operations.
1 | package com.example.otel; |
- @NewSpan: Creates new spans for the
createOrder
,getOrderStatus
, andprocessOrderAsync
methods. - @Async: Marks the
processOrderAsync
method to be executed asynchronously, allowing the method to run in a separate thread.
Design Purpose
- Traceability: The
@NewSpan
annotations ensure that each method’s execution is captured in a separate tracing span. This helps in tracking the flow of requests through different parts of the application. - Asynchronous Processing: The
@Async
annotation demonstrates how to offload work to a separate thread, which is essential for non-blocking operations. This is particularly useful for long-running tasks that should not block the main thread. - Logging: The use of logging at key points in the methods helps in debugging and understanding the flow of execution during the tracing process.
By following this design, you can effectively trace and monitor asynchronous operations in your Spring Boot application, ensuring better observability and performance analysis.
Setting Up Docker for Local Development
Create a compose.yaml
File
Set up OpenTelemetry Collector and Jaeger using Docker Compose:
1 | services: |
Create otel-collector.yml
for OpenTelemetry Collector Configuration
1 | receivers: |
Starting Services in Local Development
Use docker compose up -d
to start the services defined in compose.yaml
. Once the services are up, you can open http://localhost:16686 to view the Jaeger UI.
Next, start your Spring Boot application and perform the following test operations.
Create an Order
1 | curl -X POST "http://localhost:8080/api/orders?customerId=CUST1234" |
Output:
1 | Order created and processing started for ID: ORD-1722481291139 |
Retrieve an Order
1 | curl "http://localhost:8080/api/orders/ORD-1722481291139" |
Output:
1 | Status for order ORD-1722481291139: Processing |
After completing these operations, return to the Jaeger UI to view the trace information. You should see two new traces: one for otel-demo: http post /api/orders
and another for otel-demo: http get /api/orders/{orderId}
.
Jaeger UI Overview
This image displays the Jaeger UI search interface. You can see two traces under the otel-demo
service: one for the POST request to create an order and one for the GET request to retrieve the order. These traces were generated by the CURL commands.
Jaeger UI Trace Details
This image shows the detailed trace information for the http post /api/orders
request. This trace includes multiple spans, such as create-order
and process-order-async
, indicating that asynchronous processing was correctly traced. Each span shows the duration and order of execution, providing a clear picture of the request’s flow.
- Trace Overview: In Jaeger UI, you can see an overview of the traces for the
otel-demo
service. Each trace represents a complete request-response cycle. - Span Details: By clicking on a trace, you can view detailed information about each step (span) within the trace, including execution time and sequence. These spans help you understand the application’s execution flow and verify that asynchronous operations are working as expected.
- Asynchronous Processing: The
create-order
API uses asynchronous processing for order handling. This is evident from theprocess-order-async
span in the trace details. By examining these spans, we can ensure that the asynchronous operations are being traced correctly, providing insight into the application’s behavior during these processes.
Preparing OpenTelemetry Collector for GCP
This section details the steps to prepare and configure the OpenTelemetry Collector for deployment on Google Cloud Platform (GCP).
Setting Up GCP Permissions
Before deploying your application to Google Cloud Platform (GCP), you need to set up the necessary permissions. The following steps will guide you through configuring GCP permissions.
CLI Cleanup
Revoke all existing authorizations:
1 | gcloud auth revoke --all |
Update the gcloud CLI:
1 | gcloud components update -q |
Login
Log in using the following command:
1 | gcloud auth login |
Set your project:
1 | export PROJECT_ID=your-project-id |
Verify that the authorization and project settings are correct:
1 | gcloud auth list |
Create Service Account for Cloud Run Runtime
Create the service account:
1 | export SERVICE_ACCOUNT_ID_RUNTIME=your-service-account-id |
Bind Roles to the Service Account
Bind the necessary roles to the service account:
1 | gcloud projects add-iam-policy-binding $PROJECT_ID \ |
Create Artifact Registry Repository
Create a Docker repository:
1 | export REGION=asia-east1 |
These steps will help you set up the necessary GCP permissions, ensuring that your application has the required access and permissions when deployed to Google Cloud Platform.
Create Dockerfile for OpenTelemetry Collector
Create a Dockerfile
for the OpenTelemetry Collector:
1 | FROM otel/opentelemetry-collector-contrib:latest |
Create otel-config.yaml
for GCP Configuration
Create the configuration file otel-config.yaml
with the following content:
1 | receivers: |
Build and Push Docker Images
Build and push the Docker images to Google Artifact Registry:
1 | export PROJECT_ID=your-gcp-project-id |
- otel-config.yaml: This configuration file sets up the OpenTelemetry Collector to receive traces and metrics via OTLP over gRPC and HTTP. It includes exporters for sending data to Google Cloud Logging and various processors and extensions for managing the data.
- Build and Push Docker Images: The commands build the Docker image using the specified Dockerfile and configuration file, then push the image to Google Artifact Registry. This ensures that your OpenTelemetry Collector configuration is containerized and ready for deployment on GCP.
Deploying to Google Cloud Platform
To deploy your application and OpenTelemetry Collector to Google Cloud Platform, follow these steps to create and configure a Cloud Run service.
Create a Cloud Run Service Configuration File
Create a cloud-run.yaml
file with the following content:
1 | apiVersion: serving.knative.dev/v1 |
Deploying the Cloud Run Service
After creating the cloud-run.yaml
file, deploy the Cloud Run service using the following command:
1 | gcloud run services replace cloud-run.yaml |
Once your application and OpenTelemetry Collector are deployed to Google Cloud Platform, you can view the tracing information in the GCP Console. Here is an example of what you might see:
In the provided screenshot, the trace for the http post /api/orders
request is shown. This trace includes multiple spans such as create-order
and process-order-async
, indicating that asynchronous processing was correctly traced. Each span shows the duration and order of execution, providing a clear picture of the request’s flow.
- Trace Overview: The trace overview shows that the total duration of the request was 2.572 seconds and it involved 7 spans. This overview helps you quickly understand the performance of the request.
- Span Details: The span details show the execution time of each operation within the trace. For example, the
create-order
span took approximately 786 milliseconds, and theprocess-order-async
span took about 2 seconds. This detailed breakdown helps you identify which parts of the request took the most time.
In the GCP Trace Explorer, you can expand to view logs and traces together, providing a comprehensive view of your application’s behavior, as shown below:
Conclusion
By following this step-by-step guide, you have successfully integrated OpenTelemetry with a Spring Boot application and deployed it to Google Cloud Platform. This setup includes:
- Configuring Spring Boot for OpenTelemetry: Ensuring all necessary dependencies and settings are included for effective tracing and monitoring.
- Creating Docker Environment: Setting up Docker Compose for local development and testing.
- Deploying to GCP: Configuring and deploying your application and OpenTelemetry Collector to Google Cloud Platform using Cloud Run.
- Monitoring and Analysis: Using Jaeger UI and GCP Trace Explorer to view and analyze traces, ensuring that asynchronous processing is correctly traced and providing insights into the application’s performance.
This integration provides comprehensive observability, helping you monitor and analyze your application’s behavior in both development and production environments. With detailed tracing and monitoring, you can quickly identify and resolve performance bottlenecks and ensure a smooth and efficient application operation.
References
- Demo project
- OpenTelemetry Spring Boot starter Documentation
- Spring Boot Actuator Observability Documentation
- Micrometer Context Propagation Documentation
- Jaeger Documentation
- Google Cloud Run Documentation
- Google Cloud Trace Documentation
- OpenTelemetry Collector Demo
- Springboot App Monitoring With Prometheus And Grafana
- Micrometer Tracing In Spring Boot — Context Propagation For @Async, @Scheduled, @NewSpan, @ContinueSpan And @SpanTag
- opentelemetry-java-examples
By leveraging these resources, you can further enhance your understanding and capabilities in integrating observability into your Spring Boot applications, ensuring robust and reliable performance in cloud environments.