-
Notifications
You must be signed in to change notification settings - Fork 152
add Spring declarative config example #911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
trask
merged 16 commits into
open-telemetry:main
from
zeitlinger:spring-declarative-config
Dec 4, 2025
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
6699532
add example for spring with declarative config
zeitlinger 546816c
add example for spring with declarative config
zeitlinger ede831e
use spring style env var syntax
zeitlinger 894e75a
comment
zeitlinger 38be51f
add doc instructions
zeitlinger 276830e
copilot
zeitlinger e482fe5
gpt 5.1
zeitlinger cf65896
formatting
zeitlinger 28ef04e
manual review
zeitlinger 28f1b15
integrate with spring native
zeitlinger 026c6fa
cut out spring native - as it's not working yet
zeitlinger 0494415
format
zeitlinger 4c8443e
cleanup
zeitlinger 2c504dd
cleanup
zeitlinger dd471c7
fix
zeitlinger 4bcd4c4
Update spring-declarative-configuration/README.md
zeitlinger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,253 @@ | ||
| # Spring Boot Declarative Configuration Example | ||
|
|
||
| This example demonstrates how to | ||
| use [declarative configuration](https://opentelemetry.io/docs/specs/otel/configuration/#declarative-configuration) | ||
| with the OpenTelemetry Spring Boot Starter to configure tracing, metrics, and logging for a Spring | ||
| Boot application. | ||
|
|
||
| Instead of using the OpenTelemetry Java Agent, this module uses the | ||
| [OpenTelemetry Spring Boot Starter](https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/) | ||
| and configures declarative behavior via standard Spring Boot configuration in `application.yaml`. | ||
|
|
||
| The main configuration file for this example is: | ||
|
|
||
| - [`src/main/resources/application.yaml`](./src/main/resources/application.yaml) | ||
|
|
||
| For the underlying declarative configuration schema and additional examples, see the | ||
| [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) | ||
| repository. | ||
| Remember that when you copy examples from that repository into a Spring Boot app, you must nest them | ||
| under the `otel:` root node (two-space indentation for all keys shown below). | ||
|
|
||
| This Spring Boot application includes two endpoints: | ||
|
|
||
| - `/actuator/health` – A health check endpoint (from Spring Boot Actuator) that is configured to be | ||
| **excluded** from tracing | ||
| - `/api/example` – A simple API endpoint that **will be traced** normally | ||
|
|
||
| ## End-to-End Instructions | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - Java 17 or higher | ||
| - OpenTelemetry Spring Boot Starter **2.22.0+** on the classpath of this application (already | ||
| configured in this example’s [build file](./build.gradle.kts)) | ||
|
|
||
| ### Step 1: Build the Application | ||
|
|
||
| From this `spring-declarative-configuration` directory: | ||
|
|
||
| ```bash | ||
| ../gradlew bootJar | ||
| ``` | ||
|
|
||
| This builds the Spring Boot fat JAR for the example application. | ||
|
|
||
| ### Step 2: Run the Spring Boot Application | ||
|
|
||
| Run the application as a normal Spring Boot JAR – no `-javaagent` flag and no separate | ||
| `otel-agent-config.yaml` file | ||
| are needed. Declarative configuration is picked up from `application.yaml` by the Spring Boot | ||
| Starter. | ||
|
|
||
| ```bash | ||
| java -jar build/libs/spring-declarative-configuration.jar | ||
| ``` | ||
|
|
||
| The Spring Boot Starter will automatically: | ||
|
|
||
| - Initialize OpenTelemetry SDK | ||
| - Read declarative configuration under the `otel:` root in `application.yaml` | ||
| - Apply exporters, processors, and sampler rules defined there | ||
|
|
||
| ### Step 3: Test the Endpoints | ||
|
|
||
| In a separate terminal, call both endpoints: | ||
|
|
||
| ```bash | ||
| # This endpoint will NOT be traced (excluded by declarative configuration) | ||
| curl http://localhost:8080/actuator/health | ||
|
|
||
| # This endpoint WILL be traced normally | ||
| curl http://localhost:8080/api/example | ||
| ``` | ||
|
|
||
| ### Step 4: Verify Tracing, Metrics, and Logs | ||
|
|
||
| By default, this example configures: | ||
|
|
||
| - An OTLP HTTP exporter for traces, metrics, and logs | ||
| - A console exporter for traces for easy local inspection | ||
|
|
||
| Check the application logs to see that: | ||
|
|
||
| - Health check requests (`/actuator/health`) **do not** generate spans (dropped by sampler rules) | ||
| - Requests to `/api/example` **do** generate spans and are exported to console and/or OTLP | ||
|
|
||
| If you have an OTLP-compatible backend (e.g., the OpenTelemetry Collector, Jaeger, Tempo, etc.) | ||
| listening on | ||
| `http://localhost:4318`, you can inspect the exported telemetry there as well. | ||
|
|
||
| ## Declarative Configuration with Spring Boot | ||
|
|
||
| The declarative configuration used by this example lives in [`application.yaml`](./src/main/resources/application.yaml) | ||
| under the `otel:` root key. | ||
|
|
||
| ```yaml | ||
| otel: | ||
| # ... see src/main/resources/application.yaml for the full configuration | ||
| ``` | ||
|
|
||
| This layout follows the declarative configuration schema defined in the | ||
| [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) | ||
| repository, but adapted for Spring Boot: | ||
|
|
||
| - All OpenTelemetry configuration keys live under the `otel:` root | ||
| - Configuration blocks from the reference repo (such as `tracer_provider`, `meter_provider`, | ||
| `logger_provider`, etc.) are indented by **two spaces** beneath `otel:` | ||
| - The configuration is loaded via the OpenTelemetry Spring Boot Starter instead of the Java Agent | ||
|
|
||
| ### Opting In with `file_format` | ||
|
|
||
| Declarative configuration is **opt-in**. In this Spring Boot example, you enable declarative | ||
| configuration by setting `file_format` under `otel:` in `application.yaml`: | ||
|
|
||
| ```yaml | ||
| otel: | ||
| file_format: "1.0-rc.2" | ||
| # ... other configuration | ||
| ``` | ||
|
|
||
| The `file_format` value follows the versions defined in the | ||
| [declarative configuration specification](https://github.com/open-telemetry/opentelemetry-configuration). | ||
| If `file_format` is missing, declarative configuration is not applied. | ||
|
|
||
| ### Example: Exporters and Sampler Rules (Spring Style) | ||
|
|
||
| Below is a simplified view of the configuration used in this module. All keys are indented under | ||
| `otel:` as required by Spring Boot declarative configuration. Refer to the actual | ||
| [`application.yaml`](./src/main/resources/application.yaml) for the complete version. | ||
|
|
||
| ```yaml | ||
| otel: | ||
| file_format: "1.0-rc.2" | ||
|
|
||
| tracer_provider: | ||
| sampler: | ||
| rule_based_routing: | ||
| fallback_sampler: | ||
| always_on: | ||
| span_kind: SERVER | ||
| rules: | ||
| - action: DROP | ||
| attribute: url.path | ||
| pattern: /actuator.* | ||
| ``` | ||
|
|
||
| This configuration: | ||
|
|
||
| - Uses the `rule_based_routing` sampler from the OpenTelemetry contrib extension | ||
| - Excludes health check endpoints (`/actuator.*`) from tracing using the `DROP` action | ||
| - Samples all other requests using the `always_on` fallback sampler | ||
| - Only applies to `SERVER` span kinds | ||
|
|
||
| ## Spring Boot Starter–Specific Notes | ||
|
|
||
| ### Spring Boot Starter Version | ||
|
|
||
| - Declarative configuration is supported by the OpenTelemetry Spring Boot Starter starting with | ||
| version **2.22.0** | ||
| - Ensure your dependencies use at least this version; otherwise, `file_format` and other declarative | ||
| config features may be ignored | ||
|
|
||
| ### Property Metadata and IDE Auto-Completion | ||
|
|
||
| Most IDEs derive auto-completion for Spring properties from Spring Boot configuration metadata. At | ||
| the time of this example, that metadata is primarily based on the **non-declarative** configuration | ||
| schema. | ||
|
|
||
| As a result: | ||
|
|
||
| - Auto-suggested properties in IDEs may be incomplete or incorrect for declarative configuration | ||
| under `otel:` | ||
| - Some declarative configuration keys may not appear in auto-completion at all | ||
|
|
||
| When in doubt: | ||
|
|
||
| - Prefer the official schema and examples in | ||
| [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) | ||
| - Then adapt those examples by nesting them under the `otel:` root in your `application.yaml` | ||
|
|
||
| ### Placeholder Default Values: `:` vs `:-` | ||
|
|
||
| Spring Boot’s property placeholder syntax differs slightly from generic examples you might see in | ||
| OpenTelemetry docs. | ||
|
|
||
| - Generic examples sometimes use `${VAR_NAME:-default}` for default values | ||
| - **Spring Boot uses `:` instead of `:-`** | ||
|
|
||
| For example, in this module we configure the OTLP HTTP trace endpoint as: | ||
|
|
||
| ```yaml | ||
| otel: | ||
| tracer_provider: | ||
| processors: | ||
| - batch: | ||
| exporter: | ||
| otlp_http: | ||
| endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}/v1/traces | ||
| ``` | ||
|
|
||
| Here, `http://localhost:4318` is used as the default if the `OTEL_EXPORTER_OTLP_ENDPOINT` | ||
| environment variable is not set. | ||
|
|
||
| When copying configuration from non-Spring examples, always convert `:-` to `:` in placeholders. | ||
|
|
||
| ## Declarative vs Programmatic Configuration | ||
|
|
||
| Declarative configuration, as used in this example, allows you to express routing and sampling rules | ||
| entirely in configuration files. This is ideal for: | ||
|
|
||
| - Operational teams that need to adjust sampling or filtering without changing code | ||
| - Environments where configuration is managed externally (Kubernetes ConfigMaps, Spring Cloud | ||
| Config, etc.) | ||
|
|
||
| For more advanced or dynamic scenarios, you can still use **programmatic** configuration. The | ||
| `spring-native` module in | ||
| this repository contains an example of this: | ||
|
|
||
| - See `configureSampler` in | ||
| [`OpenTelemetryConfig`](../spring-native/src/main/java/io/opentelemetry/example/graal/OpenTelemetryConfig.java) | ||
| - It uses `RuleBasedRoutingSampler` programmatically to drop spans for actuator endpoints | ||
| (`/actuator*`), replicating the behavior we achieve declaratively via YAML in this module | ||
|
|
||
| In many cases, you can start with declarative configuration (as in this module) and only fall back | ||
| to programmatic customization for highly dynamic or application-specific logic. | ||
|
|
||
| ## Troubleshooting and Tips | ||
|
|
||
| If the behavior is not what you expect, here are a few things to check: | ||
|
|
||
| - **Health checks are still traced** | ||
| - Verify the `rules` section under `otel.tracer_provider.sampler.rule_based_routing` in | ||
| `application.yaml` | ||
| - Ensure the `pattern` matches your actual actuator paths (e.g., `/actuator.*`) | ||
| - Confirm that `span_kind` is set to `SERVER` (or another correct span kind for your traffic) | ||
|
|
||
| - **No spans are exported** | ||
| - Confirm that `otel.file_format` is set correctly (for example, `"1.0-rc.2"`) | ||
| - Check that at least one exporter is configured (e.g., `otlp_http` or `console`) | ||
| - Look for startup warnings or errors related to OpenTelemetry configuration | ||
|
|
||
| - **Properties seem to be ignored** | ||
| - Make sure you are modifying the correct `application.yaml` for the active Spring profile | ||
| - Verify that all configuration keys are indented correctly under the `otel:` root | ||
| - Double-check that any placeholders use `:` for defaults (e.g., | ||
| `${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}`) | ||
|
|
||
| If issues persist, compare your configuration to: | ||
|
|
||
| - This module’s [`application.yaml`](./src/main/resources/application.yaml) | ||
| - The Java Agent example in [`javaagent-declarative-configuration`](../javaagent-declarative-configuration) | ||
| - The reference schemas and examples in | ||
| [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import org.gradle.kotlin.dsl.named | ||
| import org.springframework.boot.gradle.plugin.SpringBootPlugin | ||
| import org.springframework.boot.gradle.tasks.bundling.BootJar | ||
|
|
||
| plugins { | ||
| id("java") | ||
| id("org.springframework.boot") version "3.5.7" | ||
| } | ||
|
|
||
| description = "OpenTelemetry Example for Spring Boot with Declarative Configuration" | ||
|
|
||
| java { | ||
| toolchain { | ||
| languageVersion.set(JavaLanguageVersion.of(17)) | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation(platform(SpringBootPlugin.BOM_COORDINATES)) | ||
| implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.22.0")) | ||
| implementation("org.springframework.boot:spring-boot-starter-actuator") | ||
| implementation("org.springframework.boot:spring-boot-starter-web") | ||
| implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter") | ||
| } | ||
|
|
||
| tasks.named<BootJar>("bootJar") { | ||
| archiveFileName = "spring-declarative-configuration.jar" | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| FROM eclipse-temurin:21.0.9_10-jre@sha256:4332b7939ba5b7fabde48f4da21ebe45a4f8943d5b3319720c321ac577e65fb1 | ||
|
|
||
| WORKDIR /usr/src/app/ | ||
|
|
||
| ADD ./build/libs/spring-declarative-configuration.jar ./app.jar | ||
|
|
||
| EXPOSE 8080 | ||
| ENTRYPOINT [ "java", "-jar", "./app.jar" ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| version: '3' | ||
| services: | ||
| app: | ||
| build: | ||
| context: ../ | ||
| dockerfile: oats/Dockerfile | ||
| environment: | ||
| OTEL_EXPORTER_OTLP_ENDPOINT: http://lgtm:4318 | ||
| ports: | ||
| - "8080:8080" | ||
| healthcheck: | ||
| test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # OATS is an acceptance testing framework for OpenTelemetry - https://github.com/grafana/oats | ||
| oats-schema-version: 2 | ||
|
|
||
| docker-compose: | ||
| files: | ||
| - ./docker-compose.yml | ||
|
|
||
| input: | ||
| # This endpoint should be traced normally | ||
| - path: /api/example | ||
| # This endpoint should NOT be traced (excluded by declarative config) | ||
| - path: /actuator/health | ||
|
|
||
| expected: | ||
| traces: | ||
| # Verify that /api/example creates a trace with SERVER span | ||
| - traceql: '{ span.http.route = "/api/example" }' | ||
| equals: "GET /api/example" | ||
| attributes: | ||
| http.request.method: "GET" | ||
| http.route: "/api/example" | ||
| - traceql: '{ span.http.route = "/actuator/health" }' | ||
| count: | ||
| max: 0 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice 🎉 |
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this readme is already pretty long, i wonder if this section could be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copilot going loose... I'll revisit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd leave it in - I do plan to move some of this content to the general docs after the next release, which contains some bug related fixes.