|
| 1 | +# Spring Boot Declarative Configuration Example |
| 2 | + |
| 3 | +This example demonstrates how to |
| 4 | +use [declarative configuration](https://opentelemetry.io/docs/specs/otel/configuration/#declarative-configuration) |
| 5 | +with the OpenTelemetry Spring Boot Starter to configure tracing, metrics, and logging for a Spring |
| 6 | +Boot application. |
| 7 | + |
| 8 | +Instead of using the OpenTelemetry Java Agent, this module uses the |
| 9 | +[OpenTelemetry Spring Boot Starter](https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/) |
| 10 | +and configures declarative behavior via standard Spring Boot configuration in `application.yaml`. |
| 11 | + |
| 12 | +The main configuration file for this example is: |
| 13 | + |
| 14 | +- [`src/main/resources/application.yaml`](./src/main/resources/application.yaml) |
| 15 | + |
| 16 | +For the underlying declarative configuration schema and additional examples, see the |
| 17 | +[opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) |
| 18 | +repository. |
| 19 | +Remember that when you copy examples from that repository into a Spring Boot app, you must nest them |
| 20 | +under the `otel:` root node (two-space indentation for all keys shown below). |
| 21 | + |
| 22 | +This Spring Boot application includes two endpoints: |
| 23 | + |
| 24 | +- `/actuator/health` – A health check endpoint (from Spring Boot Actuator) that is configured to be |
| 25 | + **excluded** from tracing |
| 26 | +- `/api/example` – A simple API endpoint that **will be traced** normally |
| 27 | + |
| 28 | +## End-to-End Instructions |
| 29 | + |
| 30 | +### Prerequisites |
| 31 | + |
| 32 | +- Java 17 or higher |
| 33 | +- OpenTelemetry Spring Boot Starter **2.22.0+** on the classpath of this application (already |
| 34 | + configured in this example’s [build file](./build.gradle.kts)) |
| 35 | + |
| 36 | +### Step 1: Build the Application |
| 37 | + |
| 38 | +From this `spring-declarative-configuration` directory: |
| 39 | + |
| 40 | +```bash |
| 41 | +../gradlew bootJar |
| 42 | +``` |
| 43 | + |
| 44 | +This builds the Spring Boot fat JAR for the example application. |
| 45 | + |
| 46 | +### Step 2: Run the Spring Boot Application |
| 47 | + |
| 48 | +Run the application as a normal Spring Boot JAR – no `-javaagent` flag and no separate |
| 49 | +`otel-agent-config.yaml` file |
| 50 | +are needed. Declarative configuration is picked up from `application.yaml` by the Spring Boot |
| 51 | +Starter. |
| 52 | + |
| 53 | +```bash |
| 54 | +java -jar build/libs/spring-declarative-configuration.jar |
| 55 | +``` |
| 56 | + |
| 57 | +The Spring Boot Starter will automatically: |
| 58 | + |
| 59 | +- Initialize OpenTelemetry SDK |
| 60 | +- Read declarative configuration under the `otel:` root in `application.yaml` |
| 61 | +- Apply exporters, processors, and sampler rules defined there |
| 62 | + |
| 63 | +### Step 3: Test the Endpoints |
| 64 | + |
| 65 | +In a separate terminal, call both endpoints: |
| 66 | + |
| 67 | +```bash |
| 68 | +# This endpoint will NOT be traced (excluded by declarative configuration) |
| 69 | +curl http://localhost:8080/actuator/health |
| 70 | + |
| 71 | +# This endpoint WILL be traced normally |
| 72 | +curl http://localhost:8080/api/example |
| 73 | +``` |
| 74 | + |
| 75 | +### Step 4: Verify Tracing, Metrics, and Logs |
| 76 | + |
| 77 | +By default, this example configures: |
| 78 | + |
| 79 | +- An OTLP HTTP exporter for traces, metrics, and logs |
| 80 | +- A console exporter for traces for easy local inspection |
| 81 | + |
| 82 | +Check the application logs to see that: |
| 83 | + |
| 84 | +- Health check requests (`/actuator/health`) **do not** generate spans (dropped by sampler rules) |
| 85 | +- Requests to `/api/example` **do** generate spans and are exported to console and/or OTLP |
| 86 | + |
| 87 | +If you have an OTLP-compatible backend (e.g., the OpenTelemetry Collector, Jaeger, Tempo, etc.) |
| 88 | +listening on |
| 89 | +`http://localhost:4318`, you can inspect the exported telemetry there as well. |
| 90 | + |
| 91 | +## Declarative Configuration with Spring Boot |
| 92 | + |
| 93 | +The declarative configuration used by this example lives in [`application.yaml`](./src/main/resources/application.yaml) |
| 94 | +under the `otel:` root key. |
| 95 | + |
| 96 | +```yaml |
| 97 | +otel: |
| 98 | +# ... see src/main/resources/application.yaml for the full configuration |
| 99 | +``` |
| 100 | + |
| 101 | +This layout follows the declarative configuration schema defined in the |
| 102 | +[opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) |
| 103 | +repository, but adapted for Spring Boot: |
| 104 | + |
| 105 | +- All OpenTelemetry configuration keys live under the `otel:` root |
| 106 | +- Configuration blocks from the reference repo (such as `tracer_provider`, `meter_provider`, |
| 107 | + `logger_provider`, etc.) are indented by **two spaces** beneath `otel:` |
| 108 | +- The configuration is loaded via the OpenTelemetry Spring Boot Starter instead of the Java Agent |
| 109 | + |
| 110 | +### Opting In with `file_format` |
| 111 | + |
| 112 | +Declarative configuration is **opt-in**. In this Spring Boot example, you enable declarative |
| 113 | +configuration by setting `file_format` under `otel:` in `application.yaml`: |
| 114 | + |
| 115 | +```yaml |
| 116 | +otel: |
| 117 | + file_format: "1.0-rc.2" |
| 118 | + # ... other configuration |
| 119 | +``` |
| 120 | + |
| 121 | +The `file_format` value follows the versions defined in the |
| 122 | +[declarative configuration specification](https://github.com/open-telemetry/opentelemetry-configuration). |
| 123 | +If `file_format` is missing, declarative configuration is not applied. |
| 124 | + |
| 125 | +### Example: Exporters and Sampler Rules (Spring Style) |
| 126 | + |
| 127 | +Below is a simplified view of the configuration used in this module. All keys are indented under |
| 128 | +`otel:` as required by Spring Boot declarative configuration. Refer to the actual |
| 129 | +[`application.yaml`](./src/main/resources/application.yaml) for the complete version. |
| 130 | + |
| 131 | +```yaml |
| 132 | +otel: |
| 133 | + file_format: "1.0-rc.2" |
| 134 | + |
| 135 | + tracer_provider: |
| 136 | + sampler: |
| 137 | + rule_based_routing: |
| 138 | + fallback_sampler: |
| 139 | + always_on: |
| 140 | + span_kind: SERVER |
| 141 | + rules: |
| 142 | + - action: DROP |
| 143 | + attribute: url.path |
| 144 | + pattern: /actuator.* |
| 145 | +``` |
| 146 | +
|
| 147 | +This configuration: |
| 148 | +
|
| 149 | +- Uses the `rule_based_routing` sampler from the OpenTelemetry contrib extension |
| 150 | +- Excludes health check endpoints (`/actuator.*`) from tracing using the `DROP` action |
| 151 | +- Samples all other requests using the `always_on` fallback sampler |
| 152 | +- Only applies to `SERVER` span kinds |
| 153 | + |
| 154 | +## Spring Boot Starter–Specific Notes |
| 155 | + |
| 156 | +### Spring Boot Starter Version |
| 157 | + |
| 158 | +- Declarative configuration is supported by the OpenTelemetry Spring Boot Starter starting with |
| 159 | + version **2.22.0** |
| 160 | +- Ensure your dependencies use at least this version; otherwise, `file_format` and other declarative |
| 161 | + config features may be ignored |
| 162 | + |
| 163 | +### Property Metadata and IDE Auto-Completion |
| 164 | + |
| 165 | +Most IDEs derive auto-completion for Spring properties from Spring Boot configuration metadata. At |
| 166 | +the time of this example, that metadata is primarily based on the **non-declarative** configuration |
| 167 | +schema. |
| 168 | + |
| 169 | +As a result: |
| 170 | + |
| 171 | +- Auto-suggested properties in IDEs may be incomplete or incorrect for declarative configuration |
| 172 | + under `otel:` |
| 173 | +- Some declarative configuration keys may not appear in auto-completion at all |
| 174 | + |
| 175 | +When in doubt: |
| 176 | + |
| 177 | +- Prefer the official schema and examples in |
| 178 | + [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) |
| 179 | +- Then adapt those examples by nesting them under the `otel:` root in your `application.yaml` |
| 180 | + |
| 181 | +### Placeholder Default Values: `:` vs `:-` |
| 182 | + |
| 183 | +Spring Boot’s property placeholder syntax differs slightly from generic examples you might see in |
| 184 | +OpenTelemetry docs. |
| 185 | + |
| 186 | +- Generic examples sometimes use `${VAR_NAME:-default}` for default values |
| 187 | +- **Spring Boot uses `:` instead of `:-`** |
| 188 | + |
| 189 | +For example, in this module we configure the OTLP HTTP trace endpoint as: |
| 190 | + |
| 191 | +```yaml |
| 192 | +otel: |
| 193 | + tracer_provider: |
| 194 | + processors: |
| 195 | + - batch: |
| 196 | + exporter: |
| 197 | + otlp_http: |
| 198 | + endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}/v1/traces |
| 199 | +``` |
| 200 | + |
| 201 | +Here, `http://localhost:4318` is used as the default if the `OTEL_EXPORTER_OTLP_ENDPOINT` |
| 202 | +environment variable is not set. |
| 203 | + |
| 204 | +When copying configuration from non-Spring examples, always convert `:-` to `:` in placeholders. |
| 205 | + |
| 206 | +## Declarative vs Programmatic Configuration |
| 207 | + |
| 208 | +Declarative configuration, as used in this example, allows you to express routing and sampling rules |
| 209 | +entirely in configuration files. This is ideal for: |
| 210 | + |
| 211 | +- Operational teams that need to adjust sampling or filtering without changing code |
| 212 | +- Environments where configuration is managed externally (Kubernetes ConfigMaps, Spring Cloud |
| 213 | + Config, etc.) |
| 214 | + |
| 215 | +For more advanced or dynamic scenarios, you can still use **programmatic** configuration. The |
| 216 | +`spring-native` module in |
| 217 | +this repository contains an example of this: |
| 218 | + |
| 219 | +- See `configureSampler` in |
| 220 | + [`OpenTelemetryConfig`](../spring-native/src/main/java/io/opentelemetry/example/graal/OpenTelemetryConfig.java) |
| 221 | +- It uses `RuleBasedRoutingSampler` programmatically to drop spans for actuator endpoints |
| 222 | + (`/actuator*`), replicating the behavior we achieve declaratively via YAML in this module |
| 223 | + |
| 224 | +In many cases, you can start with declarative configuration (as in this module) and only fall back |
| 225 | +to programmatic customization for highly dynamic or application-specific logic. |
| 226 | + |
| 227 | +## Troubleshooting and Tips |
| 228 | + |
| 229 | +If the behavior is not what you expect, here are a few things to check: |
| 230 | + |
| 231 | +- **Health checks are still traced** |
| 232 | + - Verify the `rules` section under `otel.tracer_provider.sampler.rule_based_routing` in |
| 233 | + `application.yaml` |
| 234 | + - Ensure the `pattern` matches your actual actuator paths (e.g., `/actuator.*`) |
| 235 | + - Confirm that `span_kind` is set to `SERVER` (or another correct span kind for your traffic) |
| 236 | + |
| 237 | +- **No spans are exported** |
| 238 | + - Confirm that `otel.file_format` is set correctly (for example, `"1.0-rc.2"`) |
| 239 | + - Check that at least one exporter is configured (e.g., `otlp_http` or `console`) |
| 240 | + - Look for startup warnings or errors related to OpenTelemetry configuration |
| 241 | + |
| 242 | +- **Properties seem to be ignored** |
| 243 | + - Make sure you are modifying the correct `application.yaml` for the active Spring profile |
| 244 | + - Verify that all configuration keys are indented correctly under the `otel:` root |
| 245 | + - Double-check that any placeholders use `:` for defaults (e.g., |
| 246 | + `${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}`) |
| 247 | + |
| 248 | +If issues persist, compare your configuration to: |
| 249 | + |
| 250 | +- This module’s [`application.yaml`](./src/main/resources/application.yaml) |
| 251 | +- The Java Agent example in [`javaagent-declarative-configuration`](../javaagent-declarative-configuration) |
| 252 | +- The reference schemas and examples in |
| 253 | + [opentelemetry-configuration](https://github.com/open-telemetry/opentelemetry-configuration) |
0 commit comments