Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4559fdf
Add InstrumentationDefaults helper to declarative-config-bridge
zeitlinger Apr 10, 2026
0572994
fix: add @CanIgnoreReturnValue to InstrumentationDefaults.setDefault
zeitlinger Apr 13, 2026
deef7d8
fix: add @CanIgnoreReturnValue to applyToModel
zeitlinger Apr 13, 2026
97ab9a4
docs: add InstrumentationDefaults usage to declarative-config-bridge …
zeitlinger Apr 13, 2026
1e43af3
docs(declarative-config-bridge): restructure InstrumentationDefaults …
zeitlinger Apr 17, 2026
425d9c2
refactor(declarative-config-bridge): nest InstrumentationDefaults API
zeitlinger Apr 18, 2026
ea1616d
docs(declarative-config-bridge): use get instead of getStructured in …
zeitlinger Apr 18, 2026
a370c6a
Merge branch 'main' into instrumentation-defaults
trask May 4, 2026
a7d2e54
fix: mirror development key translation in InstrumentationDefaults
zeitlinger May 4, 2026
0f27445
test: use parameterized InstrumentationDefaults translation cases
zeitlinger May 4, 2026
5b0f8fc
test: add bridge roundtrip coverage for InstrumentationDefaults
zeitlinger May 4, 2026
fdadd88
Use HashMap for InstrumentationDefaults storage; iteration order is n…
zeitlinger May 8, 2026
ef6c451
fix: isolate declarative model support in defaults helper
zeitlinger May 12, 2026
93b37ee
fix: export incubator model dependency from config bridge
zeitlinger May 12, 2026
b4a4100
Merge remote-tracking branch 'origin/main' into lane-pr17816-fix
zeitlinger May 12, 2026
256f42b
fix: align config bridge defaults helper with declarative config API
zeitlinger May 12, 2026
57a940b
Preserve typed instrumentation defaults in declarative bridge
zeitlinger Jul 14, 2026
622aa8e
Merge remote-tracking branch 'origin/main' into instrumentation-defaults
zeitlinger Jul 14, 2026
bd0483d
Align declarative bridge applier with current SDK model packages
zeitlinger Jul 14, 2026
3dfcb3c
format
zeitlinger Jul 14, 2026
0eb826d
Add instance applyToModel convenience
zeitlinger Jul 15, 2026
a6342ac
Apply spotless formatting
zeitlinger Jul 15, 2026
dbcae20
make package protected
zeitlinger Jul 16, 2026
528f660
chore: merge origin/main into instrumentation-defaults
zeitlinger Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions declarative-config-bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,52 @@ public class InferredSpansComponentProvider implements ComponentProvider {
}
}
```

Comment thread
zeitlinger marked this conversation as resolved.
## InstrumentationDefaults

`InstrumentationDefaults` lets distribution authors define instrumentation property defaults once
and have them work in both configuration modes — traditional auto-configuration and declarative
configuration.

### Usage

```java
InstrumentationDefaults defaults = new InstrumentationDefaults();
defaults.setDefault("micrometer", "base_time_unit", "s");
defaults.setDefault("log4j_appender", "experimental_log_attributes", "true");
Comment thread
zeitlinger marked this conversation as resolved.
Outdated
```

Keys use underscore notation (matching the declarative config model). They are translated to
hyphen notation (`otel.instrumentation.<name>.<key>`) when producing system property keys.

### Auto-configuration (traditional)

Register the defaults as a properties supplier:

```java
@AutoService(AutoConfigurationCustomizerProvider.class)
public class MyDistroAutoConfig implements AutoConfigurationCustomizerProvider {
@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
autoConfiguration.addPropertiesSupplier(defaults::toConfigProperties);
}
}
Comment thread
zeitlinger marked this conversation as resolved.
```

### Declarative configuration

Register the defaults as a model customizer:

```java
@AutoService(DeclarativeConfigurationCustomizerProvider.class)
public class MyDistroDeclarativeConfig implements DeclarativeConfigurationCustomizerProvider {
@Override
public void customize(DeclarativeConfigurationCustomizer customizer) {
customizer.addModelCustomizer(model -> defaults.applyToModel(model));
}
}
```

Defaults are injected under `instrumentation/development.java` in the model. Explicit user
configuration always takes precedence — defaults are only applied for properties not already
present.
1 change: 1 addition & 0 deletions declarative-config-bridge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group = "io.opentelemetry.instrumentation"

dependencies {
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
Comment thread
zeitlinger marked this conversation as resolved.
Outdated
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
implementation("io.opentelemetry:opentelemetry-api-incubator")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.config.bridge;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalInstrumentationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* Defines instrumentation defaults that work with both traditional property-based configuration and
* declarative configuration.
*
* <p>Usage:
*
* <pre>{@code
* InstrumentationDefaults defaults = new InstrumentationDefaults();
* defaults.setDefault("micrometer", "base_time_unit", "s");
* defaults.setDefault("log4j_appender", "experimental_log_attributes", "true");
*
* // Declarative config mode: inject into model
* customizer.addModelCustomizer(model -> defaults.applyToModel(model));
*
* // Traditional mode: translate to ConfigProperties
* autoConfiguration.addPropertiesSupplier(defaults::toConfigProperties);
* }</pre>
*/
public final class InstrumentationDefaults {

private final Map<String, Map<String, String>> instrumentations = new LinkedHashMap<>();

/**
* Sets a default value for an instrumentation property. Keys use underscore notation (e.g. {@code
* base_time_unit}); they are translated to hyphen notation when producing property keys.
*
* @return {@code this} for chaining
*/
@CanIgnoreReturnValue
public InstrumentationDefaults setDefault(String instrumentation, String key, String value) {
instrumentations.computeIfAbsent(instrumentation, k -> new LinkedHashMap<>()).put(key, value);
return this;
}

/** Translates defaults to {@code otel.instrumentation.*} keys for auto-configuration. */
public Map<String, String> toConfigProperties() {
HashMap<String, String> map = new HashMap<>();
instrumentations.forEach(
(instrumentation, properties) ->
properties.forEach(
(key, value) ->
map.put(
"otel.instrumentation."
Comment thread
zeitlinger marked this conversation as resolved.
Outdated
+ instrumentation.replace('_', '-')
+ "."
+ key.replace('_', '-'),
Comment thread
zeitlinger marked this conversation as resolved.
Outdated
value)));
return map;
}

/**
* Applies defaults to the declarative configuration model under {@code
* instrumentation/development.java}. Existing values in the model take precedence; defaults are
* only set for properties not already present.
*/
@CanIgnoreReturnValue
public OpenTelemetryConfigurationModel applyToModel(OpenTelemetryConfigurationModel model) {
if (instrumentations.isEmpty()) {
return model;
}
Comment thread
zeitlinger marked this conversation as resolved.
Outdated

ExperimentalInstrumentationModel instrumentation = model.getInstrumentationDevelopment();
if (instrumentation == null) {
instrumentation = new ExperimentalInstrumentationModel();
model.withInstrumentationDevelopment(instrumentation);
}
ExperimentalLanguageSpecificInstrumentationModel java = instrumentation.getJava();
if (java == null) {
java = new ExperimentalLanguageSpecificInstrumentationModel();
instrumentation.withJava(java);
}

Map<String, ExperimentalLanguageSpecificInstrumentationPropertyModel> props =
java.getAdditionalProperties();

for (Map.Entry<String, Map<String, String>> entry : instrumentations.entrySet()) {
String name = entry.getKey();
Map<String, String> defaults = entry.getValue();

ExperimentalLanguageSpecificInstrumentationPropertyModel propModel = props.get(name);
if (propModel == null) {
propModel = new ExperimentalLanguageSpecificInstrumentationPropertyModel();
props.put(name, propModel);
}

for (Map.Entry<String, String> defaultEntry : defaults.entrySet()) {
propModel
.getAdditionalProperties()
.putIfAbsent(defaultEntry.getKey(), defaultEntry.getValue());
}
}

return model;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.config.bridge;

import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import java.util.Map;
import org.junit.jupiter.api.Test;

class InstrumentationDefaultsTest {

@Test
void toConfigProperties() {
InstrumentationDefaults defaults = new InstrumentationDefaults();
defaults.setDefault("micrometer", "base_time_unit", "s");
defaults.setDefault("log4j_appender", "experimental_log_attributes", "true");

Map<String, String> props = defaults.toConfigProperties();

assertThat(props)
.containsEntry("otel.instrumentation.micrometer.base-time-unit", "s")
.containsEntry("otel.instrumentation.log4j-appender.experimental-log-attributes", "true")
.hasSize(2);
}

@Test
void applyToModel() {
InstrumentationDefaults defaults = new InstrumentationDefaults();
defaults.setDefault("micrometer", "base_time_unit", "s");

OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel();
defaults.applyToModel(model);

assertThat(
model
.getInstrumentationDevelopment()
.getJava()
.getAdditionalProperties()
.get("micrometer")
.getAdditionalProperties())
.containsEntry("base_time_unit", "s");
}

@Test
void applyToModelDoesNotOverrideExisting() {
InstrumentationDefaults defaults = new InstrumentationDefaults();
defaults.setDefault("micrometer", "base_time_unit", "s");

// Pre-populate model with a different value
OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel();
new InstrumentationDefaults()
.setDefault("micrometer", "base_time_unit", "ms")
.applyToModel(model);

// Apply defaults — should not override
defaults.applyToModel(model);

assertThat(
model
.getInstrumentationDevelopment()
.getJava()
.getAdditionalProperties()
.get("micrometer")
.getAdditionalProperties())
.containsEntry("base_time_unit", "ms");
}
}
Loading