-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add InstrumentationDefaults helper to declarative-config-bridge #17816
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
Open
zeitlinger
wants to merge
23
commits into
open-telemetry:main
Choose a base branch
from
zeitlinger:instrumentation-defaults
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
4559fdf
Add InstrumentationDefaults helper to declarative-config-bridge
zeitlinger 0572994
fix: add @CanIgnoreReturnValue to InstrumentationDefaults.setDefault
zeitlinger deef7d8
fix: add @CanIgnoreReturnValue to applyToModel
zeitlinger 97ab9a4
docs: add InstrumentationDefaults usage to declarative-config-bridge …
zeitlinger 1e43af3
docs(declarative-config-bridge): restructure InstrumentationDefaults …
zeitlinger 425d9c2
refactor(declarative-config-bridge): nest InstrumentationDefaults API
zeitlinger ea1616d
docs(declarative-config-bridge): use get instead of getStructured in …
zeitlinger a370c6a
Merge branch 'main' into instrumentation-defaults
trask a7d2e54
fix: mirror development key translation in InstrumentationDefaults
zeitlinger 0f27445
test: use parameterized InstrumentationDefaults translation cases
zeitlinger 5b0f8fc
test: add bridge roundtrip coverage for InstrumentationDefaults
zeitlinger fdadd88
Use HashMap for InstrumentationDefaults storage; iteration order is n…
zeitlinger ef6c451
fix: isolate declarative model support in defaults helper
zeitlinger 93b37ee
fix: export incubator model dependency from config bridge
zeitlinger b4a4100
Merge remote-tracking branch 'origin/main' into lane-pr17816-fix
zeitlinger 256f42b
fix: align config bridge defaults helper with declarative config API
zeitlinger 57a940b
Preserve typed instrumentation defaults in declarative bridge
zeitlinger 622aa8e
Merge remote-tracking branch 'origin/main' into instrumentation-defaults
zeitlinger bd0483d
Align declarative bridge applier with current SDK model packages
zeitlinger 3dfcb3c
format
zeitlinger 0eb826d
Add instance applyToModel convenience
zeitlinger a6342ac
Apply spotless formatting
zeitlinger dbcae20
make package protected
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
142 changes: 142 additions & 0 deletions
142
...src/main/java/io/opentelemetry/instrumentation/config/bridge/InstrumentationDefaults.java
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,142 @@ | ||
| /* | ||
| * 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.Map; | ||
|
|
||
| /** | ||
| * Defines instrumentation defaults that work with both traditional property-based configuration and | ||
| * declarative configuration. | ||
| * | ||
| * <p>Navigation mirrors {@link io.opentelemetry.api.incubator.config.DeclarativeConfigProperties}: | ||
| * read-side uses {@code config.get(name).getString(key)}; write-side uses {@code | ||
| * defaults.get(name).setDefault(key, value)}. | ||
| * | ||
| * <p>Usage: | ||
| * | ||
| * <pre>{@code | ||
| * InstrumentationDefaults defaults = new InstrumentationDefaults(); | ||
| * defaults.get("micrometer").setDefault("base_time_unit", "s"); | ||
| * defaults.get("log4j_appender").setDefault("experimental_log_attributes/development", "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, InstrumentationProperties> instrumentations = new HashMap<>(); | ||
|
|
||
| /** | ||
| * Returns the defaults builder for the given instrumentation, creating it if absent. Mirrors | ||
|
zeitlinger marked this conversation as resolved.
Outdated
|
||
| * {@code DeclarativeConfigProperties.get(name)} on the read side. | ||
| */ | ||
| public InstrumentationProperties get(String instrumentation) { | ||
| return instrumentations.computeIfAbsent(instrumentation, k -> new InstrumentationProperties()); | ||
| } | ||
|
|
||
| /** 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.properties.forEach( | ||
|
zeitlinger marked this conversation as resolved.
Outdated
|
||
| (key, value) -> | ||
| map.put( | ||
| "otel.instrumentation." | ||
|
zeitlinger marked this conversation as resolved.
Outdated
|
||
| + translateName(instrumentation) | ||
| + "." | ||
| + translateName(key), | ||
| 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; | ||
| } | ||
|
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, InstrumentationProperties> entry : instrumentations.entrySet()) { | ||
| String name = entry.getKey(); | ||
| Map<String, String> defaults = entry.getValue().properties; | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| /** Defaults for a single instrumentation. Keys use underscore notation. */ | ||
| public static final class InstrumentationProperties { | ||
|
zeitlinger marked this conversation as resolved.
Outdated
|
||
|
|
||
| private final Map<String, String> properties = new HashMap<>(); | ||
|
|
||
| private InstrumentationProperties() {} | ||
|
|
||
| /** | ||
| * Sets a default value for a property. Keys use underscore notation (e.g. {@code | ||
| * base_time_unit}); they are translated to hyphen notation when producing {@code | ||
| * otel.instrumentation.*} keys. Keys ending in {@code /development} follow the same {@code | ||
| * experimental.} translation as {@link ConfigPropertiesBackedDeclarativeConfigProperties}. | ||
| * | ||
| * @return {@code this} for chaining | ||
| */ | ||
| @CanIgnoreReturnValue | ||
| public InstrumentationProperties setDefault(String key, String value) { | ||
| properties.put(key, value); | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| private static String translateName(String name) { | ||
| if (name.endsWith("/development")) { | ||
| name = name.substring(0, name.length() - "/development".length()); | ||
| if (!name.contains("experimental")) { | ||
| name = "experimental." + name; | ||
| } | ||
| } | ||
| return name.replace('_', '-'); | ||
| } | ||
| } | ||
119 changes: 119 additions & 0 deletions
119
...test/java/io/opentelemetry/instrumentation/config/bridge/InstrumentationDefaultsTest.java
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,119 @@ | ||
| /* | ||
| * 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.api.incubator.config.DeclarativeConfigProperties; | ||
| import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; | ||
| import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; | ||
| import java.util.Map; | ||
| import java.util.stream.Stream; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
|
|
||
| class InstrumentationDefaultsTest { | ||
|
|
||
| private static Stream<Arguments> configPropertyDefaults() { | ||
| return Stream.of( | ||
| Arguments.of( | ||
| "micrometer", "base_time_unit", "s", "otel.instrumentation.micrometer.base-time-unit"), | ||
| Arguments.of( | ||
| "log4j_appender", | ||
| "experimental_log_attributes/development", | ||
| "true", | ||
| "otel.instrumentation.log4j-appender.experimental-log-attributes"), | ||
| Arguments.of( | ||
| "spring_scheduling", | ||
| "controller_telemetry/development", | ||
| "false", | ||
| "otel.instrumentation.spring-scheduling.experimental.controller-telemetry"), | ||
| Arguments.of( | ||
| "grpc", | ||
| "experimental_span_attributes/development", | ||
| "true", | ||
| "otel.instrumentation.grpc.experimental-span-attributes")); | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("configPropertyDefaults") | ||
| void toConfigProperties( | ||
| String instrumentation, String key, String value, String expectedPropertyKey) { | ||
| InstrumentationDefaults defaults = new InstrumentationDefaults(); | ||
| defaults.get(instrumentation).setDefault(key, value); | ||
|
|
||
| Map<String, String> props = defaults.toConfigProperties(); | ||
|
|
||
| assertThat(props).containsEntry(expectedPropertyKey, value).hasSize(1); | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("configPropertyDefaults") | ||
| void toConfigPropertiesRoundTripsThroughBridge( | ||
| String instrumentation, String key, String value, String expectedPropertyKey) { | ||
| InstrumentationDefaults defaults = new InstrumentationDefaults(); | ||
| defaults.get(instrumentation).setDefault(key, value); | ||
|
|
||
| DeclarativeConfigProperties config = | ||
| ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( | ||
| DefaultConfigProperties.createFromMap(defaults.toConfigProperties())); | ||
|
|
||
| assertThat(config.getStructured("java").getStructured(instrumentation).getString(key)) | ||
| .isEqualTo(value); | ||
| } | ||
|
|
||
| @Test | ||
| void applyToModel() { | ||
| InstrumentationDefaults defaults = new InstrumentationDefaults(); | ||
| defaults.get("micrometer").setDefault("base_time_unit", "s"); | ||
| defaults.get("log4j_appender").setDefault("experimental_log_attributes/development", "true"); | ||
|
|
||
| OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel(); | ||
| defaults.applyToModel(model); | ||
|
|
||
| assertThat( | ||
| model | ||
| .getInstrumentationDevelopment() | ||
| .getJava() | ||
| .getAdditionalProperties() | ||
| .get("micrometer") | ||
| .getAdditionalProperties()) | ||
| .containsEntry("base_time_unit", "s"); | ||
| assertThat( | ||
| model | ||
| .getInstrumentationDevelopment() | ||
| .getJava() | ||
| .getAdditionalProperties() | ||
| .get("log4j_appender") | ||
| .getAdditionalProperties()) | ||
| .containsEntry("experimental_log_attributes/development", "true"); | ||
| } | ||
|
|
||
| @Test | ||
| void applyToModelDoesNotOverrideExisting() { | ||
| // Pre-populate model with a different value | ||
| OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel(); | ||
| InstrumentationDefaults seed = new InstrumentationDefaults(); | ||
| seed.get("micrometer").setDefault("base_time_unit", "ms"); | ||
| seed.applyToModel(model); | ||
|
|
||
| // Apply a conflicting default — should not override | ||
| InstrumentationDefaults defaults = new InstrumentationDefaults(); | ||
| defaults.get("micrometer").setDefault("base_time_unit", "s"); | ||
| defaults.applyToModel(model); | ||
|
|
||
| assertThat( | ||
| model | ||
| .getInstrumentationDevelopment() | ||
| .getJava() | ||
| .getAdditionalProperties() | ||
| .get("micrometer") | ||
| .getAdditionalProperties()) | ||
| .containsEntry("base_time_unit", "ms"); | ||
| } | ||
| } |
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.
Uh oh!
There was an error while loading. Please reload this page.