From c3e3fa0a7860406c1b6ab65098be5e7089355c5d Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 11 Jul 2025 12:06:05 +0200 Subject: [PATCH 01/12] gcp auth --- gcp-auth-extension/build.gradle.kts | 1 + ...thAutoConfigurationCustomizerProvider.java | 19 +- .../gcp/auth/GcpAuthCustomizerProvider.java | 191 ++++++++++++++++++ 3 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java diff --git a/gcp-auth-extension/build.gradle.kts b/gcp-auth-extension/build.gradle.kts index fd95a653f..0f2421b41 100644 --- a/gcp-auth-extension/build.gradle.kts +++ b/gcp-auth-extension/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { compileOnly("com.google.auto.service:auto-service-annotations") compileOnly("io.opentelemetry:opentelemetry-api") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("io.opentelemetry:opentelemetry-exporter-otlp") // Only dependencies added to `implementation` configuration will be picked up by Shadow plugin diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index 1de583029..f3ea7b206 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -96,12 +96,7 @@ public class GcpAuthAutoConfigurationCustomizerProvider */ @Override public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) { - GoogleCredentials credentials; - try { - credentials = GoogleCredentials.getApplicationDefault(); - } catch (IOException e) { - throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e); - } + GoogleCredentials credentials = getCredentials(); autoConfiguration .addSpanExporterCustomizer( (spanExporter, configProperties) -> @@ -112,6 +107,16 @@ public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) { .addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource); } + static GoogleCredentials getCredentials() { + GoogleCredentials credentials; + try { + credentials = GoogleCredentials.getApplicationDefault(); + } catch (IOException e) { + throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e); + } + return credentials; + } + @Override public int order() { return Integer.MAX_VALUE - 1; @@ -193,7 +198,7 @@ private static MetricExporter addAuthorizationHeaders( return exporter; } - private static Map getRequiredHeaderMap( + static Map getRequiredHeaderMap( GoogleCredentials credentials, ConfigProperties configProperties) { Map> gcpHeaders; try { diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java new file mode 100644 index 000000000..a06008562 --- /dev/null +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -0,0 +1,191 @@ +package io.opentelemetry.contrib.gcp.auth; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LoggerProviderModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MeterProviderModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpGrpcExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpGrpcMetricExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpMetricExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleLogRecordProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + GoogleCredentials credentials = GcpAuthAutoConfigurationCustomizerProvider.getCredentials(); + // todo pass config bridge + Map headerMap = GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap( + credentials, null); + customizeMeter(model, headerMap); + // todo are loggers supported now (not covered in old variant)? + customizeLogger(model, headerMap); + customizeTracer(model, headerMap); + + return model; + }); + } + + private void customizeMeter(OpenTelemetryConfigurationModel model, + Map headerMap) { + MeterProviderModel meterProvider = model.getMeterProvider(); + if (meterProvider == null) { + return; + } + + for (MetricReaderModel reader : meterProvider.getReaders()) { + if (reader.getPeriodic() != null) { + addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), + headerMap); + } + } + } + + private List> meterModelHeaders( + PushMetricExporterModel exporter) { + ArrayList> list = new ArrayList<>(); + if (exporter == null) { + return list; + } + OtlpGrpcMetricExporterModel grpc = exporter.getOtlpGrpc(); + if (grpc != null) { + list.add(grpc.getHeaders()); + } + OtlpHttpMetricExporterModel http = exporter.getOtlpHttp(); + if (http != null) { + list.add(http.getHeaders()); + } + return list; + } + + private void customizeLogger(OpenTelemetryConfigurationModel model, + Map headerMap) { + LoggerProviderModel loggerProvider = model.getLoggerProvider(); + if (loggerProvider == null) { + return; + } + for (LogRecordProcessorModel processor : loggerProvider.getProcessors()) { + BatchLogRecordProcessorModel batch = processor.getBatch(); + if (batch != null) { + addAuth(logRecordModelHeaders(batch.getExporter()), + headerMap); + } + SimpleLogRecordProcessorModel simple = processor.getSimple(); + if (simple != null) { + addAuth(logRecordModelHeaders(simple.getExporter()), + headerMap); + } + } + } + + private List> logRecordModelHeaders( + LogRecordExporterModel exporter) { + ArrayList> list = new ArrayList<>(); + + if (exporter == null) { + return list; + } + OtlpGrpcExporterModel grpc = exporter.getOtlpGrpc(); + if (grpc != null) { + list.add(grpc.getHeaders()); + } + OtlpHttpExporterModel http = exporter.getOtlpHttp(); + if (http != null) { + list.add(http.getHeaders()); + } + return list; + } + + private void customizeTracer(OpenTelemetryConfigurationModel model, + Map headerMap) { + TracerProviderModel tracerProvider = model.getTracerProvider(); + if (tracerProvider == null) { + return; + } + + // todo here we would want a simplified version of the declarative config bridge + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java +// googleNode(model) + +// if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { + // todo +// String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; +// logger.log( +// Level.WARNING, +// "GCP Authentication Extension is not configured for signal type: {0}. {1}", +// params); +// return; +// } + + for (SpanProcessorModel processor : tracerProvider.getProcessors()) { + BatchSpanProcessorModel batch = processor.getBatch(); + if (batch != null) { + addAuth(spanExporterModelHeaders(batch.getExporter()), + headerMap); + } + SimpleSpanProcessorModel simple = processor.getSimple(); + if (simple != null) { + addAuth(spanExporterModelHeaders(simple.getExporter()), + headerMap); + } + } + } + + private void googleNode(OpenTelemetryConfigurationModel model) { + // todo use declarative config bridge + } + + private List> spanExporterModelHeaders( + SpanExporterModel exporter) { + ArrayList> list = new ArrayList<>(); + + if (exporter == null) { + return list; + } + OtlpGrpcExporterModel grpc = exporter.getOtlpGrpc(); + if (grpc != null) { + list.add(grpc.getHeaders()); + } + OtlpHttpExporterModel http = exporter.getOtlpHttp(); + if (http != null) { + list.add(http.getHeaders()); + } + return list; + } + + private void addAuth( + List> headerConsumers, Map headerMap) { + headerConsumers.forEach( + headers -> addHeaders(headers, headerMap)); + } + + private void addHeaders(List headers, Map add) { + add.forEach( + (key, value) -> { + if (headers.stream().noneMatch(header -> key.equals(header.getName()))) { + headers.add(new NameStringValuePairModel().withName(key).withValue(value)); + } + }); + } +} From 19d553fd571c381b29a955da6c1cfd51c475632b Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:07:55 +0000 Subject: [PATCH 02/12] ./gradlew spotlessApply --- ...thAutoConfigurationCustomizerProvider.java | 2 +- .../gcp/auth/GcpAuthCustomizerProvider.java | 67 +++++++++---------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index f3ea7b206..b06359c38 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -107,7 +107,7 @@ public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) { .addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource); } - static GoogleCredentials getCredentials() { + static GoogleCredentials getCredentials() { GoogleCredentials credentials; try { credentials = GoogleCredentials.getApplicationDefault(); diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index a06008562..e2512aab6 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -1,3 +1,8 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + package io.opentelemetry.contrib.gcp.auth; import com.google.auth.oauth2.GoogleCredentials; @@ -34,10 +39,11 @@ public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustom public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { - GoogleCredentials credentials = GcpAuthAutoConfigurationCustomizerProvider.getCredentials(); + GoogleCredentials credentials = + GcpAuthAutoConfigurationCustomizerProvider.getCredentials(); // todo pass config bridge - Map headerMap = GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap( - credentials, null); + Map headerMap = + GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap(credentials, null); customizeMeter(model, headerMap); // todo are loggers supported now (not covered in old variant)? customizeLogger(model, headerMap); @@ -47,8 +53,8 @@ public void customize(DeclarativeConfigurationCustomizer customizer) { }); } - private void customizeMeter(OpenTelemetryConfigurationModel model, - Map headerMap) { + private void customizeMeter( + OpenTelemetryConfigurationModel model, Map headerMap) { MeterProviderModel meterProvider = model.getMeterProvider(); if (meterProvider == null) { return; @@ -56,14 +62,12 @@ private void customizeMeter(OpenTelemetryConfigurationModel model, for (MetricReaderModel reader : meterProvider.getReaders()) { if (reader.getPeriodic() != null) { - addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), - headerMap); + addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); } } } - private List> meterModelHeaders( - PushMetricExporterModel exporter) { + private List> meterModelHeaders(PushMetricExporterModel exporter) { ArrayList> list = new ArrayList<>(); if (exporter == null) { return list; @@ -79,8 +83,8 @@ private List> meterModelHeaders( return list; } - private void customizeLogger(OpenTelemetryConfigurationModel model, - Map headerMap) { + private void customizeLogger( + OpenTelemetryConfigurationModel model, Map headerMap) { LoggerProviderModel loggerProvider = model.getLoggerProvider(); if (loggerProvider == null) { return; @@ -88,13 +92,11 @@ private void customizeLogger(OpenTelemetryConfigurationModel model, for (LogRecordProcessorModel processor : loggerProvider.getProcessors()) { BatchLogRecordProcessorModel batch = processor.getBatch(); if (batch != null) { - addAuth(logRecordModelHeaders(batch.getExporter()), - headerMap); + addAuth(logRecordModelHeaders(batch.getExporter()), headerMap); } SimpleLogRecordProcessorModel simple = processor.getSimple(); if (simple != null) { - addAuth(logRecordModelHeaders(simple.getExporter()), - headerMap); + addAuth(logRecordModelHeaders(simple.getExporter()), headerMap); } } } @@ -117,8 +119,8 @@ private List> logRecordModelHeaders( return list; } - private void customizeTracer(OpenTelemetryConfigurationModel model, - Map headerMap) { + private void customizeTracer( + OpenTelemetryConfigurationModel model, Map headerMap) { TracerProviderModel tracerProvider = model.getTracerProvider(); if (tracerProvider == null) { return; @@ -126,28 +128,26 @@ private void customizeTracer(OpenTelemetryConfigurationModel model, // todo here we would want a simplified version of the declarative config bridge // https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java -// googleNode(model) - -// if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { - // todo -// String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; -// logger.log( -// Level.WARNING, -// "GCP Authentication Extension is not configured for signal type: {0}. {1}", -// params); -// return; -// } + // googleNode(model) + + // if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { + // todo + // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; + // logger.log( + // Level.WARNING, + // "GCP Authentication Extension is not configured for signal type: {0}. {1}", + // params); + // return; + // } for (SpanProcessorModel processor : tracerProvider.getProcessors()) { BatchSpanProcessorModel batch = processor.getBatch(); if (batch != null) { - addAuth(spanExporterModelHeaders(batch.getExporter()), - headerMap); + addAuth(spanExporterModelHeaders(batch.getExporter()), headerMap); } SimpleSpanProcessorModel simple = processor.getSimple(); if (simple != null) { - addAuth(spanExporterModelHeaders(simple.getExporter()), - headerMap); + addAuth(spanExporterModelHeaders(simple.getExporter()), headerMap); } } } @@ -176,8 +176,7 @@ private List> spanExporterModelHeaders( private void addAuth( List> headerConsumers, Map headerMap) { - headerConsumers.forEach( - headers -> addHeaders(headers, headerMap)); + headerConsumers.forEach(headers -> addHeaders(headers, headerMap)); } private void addHeaders(List headers, Map add) { From 3a5ecb52b2202e9490a6d009b7ee1854eb93c69b Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 17:25:05 +0200 Subject: [PATCH 03/12] copy declarative config bridge from instrumentation --- declarative-config-bridge/build.gradle.kts | 17 ++ .../autoconfigure/ConfigPropertiesUtil.java | 55 ++++++ .../DeclarativeConfigPropertiesBridge.java | 158 ++++++++++++++++++ .../ConfigPropertiesUtilTest.java | 87 ++++++++++ ...DeclarativeConfigPropertiesBridgeTest.java | 137 +++++++++++++++ settings.gradle.kts | 1 + 6 files changed, 455 insertions(+) create mode 100644 declarative-config-bridge/build.gradle.kts create mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java create mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java create mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java create mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts new file mode 100644 index 000000000..66fb4658b --- /dev/null +++ b/declarative-config-bridge/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") +} + +description = "OpenTelemetry extension that provides a bridge for declarative configuration." +otelJava.moduleName.set("io.opentelemetry.contrib.sdk.declarative.config.bridge") + +dependencies { + // We use `compileOnly` dependency because during runtime all necessary classes are provided by + // javaagent itself. + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java new file mode 100644 index 000000000..af6cc6397 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; + +public class ConfigPropertiesUtil { + private ConfigPropertiesUtil() { + } + + /** + * Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. + */ + public static ConfigProperties resolveConfigProperties( + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + ConfigProperties sdkConfigProperties = + AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); + if (sdkConfigProperties != null) { + return sdkConfigProperties; + } + ConfigProvider configProvider = + AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk); + if (configProvider != null) { + DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); + + if (instrumentationConfig == null) { + instrumentationConfig = DeclarativeConfigProperties.empty(); + } + + return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + } + // Should never happen + throw new IllegalStateException( + "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); + } + + public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model) { + SdkConfigProvider configProvider = SdkConfigProvider.create(model); + DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); + if (instrumentationConfig == null) { + instrumentationConfig = DeclarativeConfigProperties.empty(); + } + + return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + } +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java new file mode 100644 index 000000000..0721e4a69 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -0,0 +1,158 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +import javax.annotation.Nullable; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +/** + * A {@link ConfigProperties} which resolves properties based on {@link + * DeclarativeConfigProperties}. + * + *

Only properties starting with "otel.instrumentation." are resolved. Others return null (or + * default value if provided). + * + *

To resolve: + * + *

    + *
  • "otel.instrumentation" refers to the ".instrumentation.java" node + *
  • The portion of the property after "otel.instrumentation." is split into segments based on + * ".". + *
  • For each N-1 segment, we walk down the tree to find the relevant leaf {@link + * DeclarativeConfigProperties}. + *
  • We extract the property from the resolved {@link DeclarativeConfigProperties} using the + * last segment as the property key. + *
+ * + *

For example, given the following YAML, asking for {@code + * ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value": + * + *

+ *   instrumentation:
+ *     java:
+ *       common:
+ *         string_key: value
+ * 
+ */ +final class DeclarativeConfigPropertiesBridge implements ConfigProperties { + + private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; + + // The node at .instrumentation.java + private final DeclarativeConfigProperties instrumentationJavaNode; + + DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { + instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); + } + + @Nullable + @Override + public String getString(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getString); + } + + @Nullable + @Override + public Boolean getBoolean(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean); + } + + @Nullable + @Override + public Integer getInt(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt); + } + + @Nullable + @Override + public Long getLong(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + } + + @Nullable + @Override + public Double getDouble(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble); + } + + @Nullable + @Override + public Duration getDuration(String propertyName) { + Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + if (millis == null) { + return null; + } + return Duration.ofMillis(millis); + } + + @Override + public List getList(String propertyName) { + List propertyValue = + getPropertyValue( + propertyName, + (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); + return propertyValue == null ? Collections.emptyList() : propertyValue; + } + + @Override + public Map getMap(String propertyName) { + DeclarativeConfigProperties propertyValue = + getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured); + if (propertyValue == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + propertyValue + .getPropertyKeys() + .forEach( + key -> { + String value = propertyValue.getString(key); + if (value == null) { + return; + } + result.put(key, value); + }); + return Collections.unmodifiableMap(result); + } + + @Nullable + private T getPropertyValue( + String property, BiFunction extractor) { + if (instrumentationJavaNode == null) { + return null; + } + + if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { + property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); + } + // Split the remainder of the property on "." + String[] segments = property.split("\\."); + if (segments.length == 0) { + return null; + } + + // Extract the value by walking to the N-1 entry + DeclarativeConfigProperties target = instrumentationJavaNode; + if (segments.length > 1) { + for (int i = 0; i < segments.length - 1; i++) { + target = target.getStructured(segments[i], empty()); + } + } + String lastPart = segments[segments.length - 1]; + + return extractor.apply(target, lastPart); + } +} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java new file mode 100644 index 000000000..8d88e9783 --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java @@ -0,0 +1,87 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +@SuppressWarnings("DoNotMockAutoValue") +class ConfigPropertiesUtilTest { + @Test + void shouldUseConfigPropertiesForAutoConfiguration() { + ConfigProperties configPropertiesMock = mock(ConfigProperties.class); + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfig(sdkMock)) + .thenReturn(configPropertiesMock); + + ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock); + + assertThat(configProperties).isSameAs(configPropertiesMock); + } + } + + @Test + void shouldUseConfigProviderForDeclarativeConfiguration() { + String propertyName = "testProperty"; + String expectedValue = "the value"; + DeclarativeConfigProperties javaNodeMock = mock(DeclarativeConfigProperties.class); + when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); + + DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); + when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock); + + ConfigProvider configProviderMock = mock(ConfigProvider.class); + when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); + + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) + .thenReturn(configProviderMock); + + ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock); + + assertThat(configProperties.getString(propertyName)).isEqualTo(expectedValue); + } + } + + @Test + void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig() { + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + ConfigProvider configProviderMock = mock(ConfigProvider.class); + when(configProviderMock.getInstrumentationConfig()).thenReturn(null); + + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) + .thenReturn(configProviderMock); + + ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock); + + assertThat(configProperties.getString("testProperty")).isEqualTo(null); + } + } +} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java new file mode 100644 index 000000000..5bff96e16 --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -0,0 +1,137 @@ +package io.opentelemetry.contrib.sdk.autoconfigure;/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class DeclarativeConfigPropertiesBridgeTest { + + private static final String YAML = + "file_format: 0.4\n" + + "instrumentation/development:\n" + + " java:\n" + + " common:\n" + + " default-enabled: true\n" + + " runtime-telemetry:\n" + + " enabled: false\n" + + " example-instrumentation:\n" + + " string_key: value\n" + + " bool_key: true\n" + + " int_key: 1\n" + + " double_key: 1.1\n" + + " list_key:\n" + + " - value1\n" + + " - value2\n" + + " - true\n" + + " map_key:\n" + + " string_key1: value1\n" + + " string_key2: value2\n" + + " bool_key: true\n" + + " acme:\n" + + " full_name:\n" + + " preserved: true"; + + private ConfigProperties bridge; + private ConfigProperties emptyBridge; + + @BeforeEach + void setup() { + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(YAML.getBytes(StandardCharsets.UTF_8))); + SdkConfigProvider configProvider = SdkConfigProvider.create(model); + bridge = + new ConfigPropertiesUtil.DeclarativeConfigPropertiesBridge( + Objects.requireNonNull(configProvider.getInstrumentationConfig())); + + OpenTelemetryConfigurationModel emptyModel = + new OpenTelemetryConfigurationModel() + .withAdditionalProperty("instrumentation/development", new InstrumentationModel()); + SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel); + emptyBridge = + new ConfigPropertiesUtil.DeclarativeConfigPropertiesBridge( + Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig())); + } + + @Test + void getProperties() { + // only properties starting with "otel.instrumentation." are resolved + // asking for properties which don't exist or inaccessible shouldn't result in an error + assertThat(bridge.getString("file_format")).isNull(); + assertThat(bridge.getString("file_format", "foo")).isEqualTo("foo"); + assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled")).isNull(); + assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled", true)) + .isTrue(); + + // common cases + assertThat(bridge.getBoolean("otel.instrumentation.common.default-enabled")).isTrue(); + assertThat(bridge.getBoolean("otel.instrumentation.runtime-telemetry.enabled")).isFalse(); + + // check all the types + Map expectedMap = new HashMap<>(); + expectedMap.put("string_key1", "value1"); + expectedMap.put("string_key2", "value2"); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.string_key")) + .isEqualTo("value"); + assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.bool_key")).isTrue(); + assertThat(bridge.getInt("otel.instrumentation.example-instrumentation.int_key")).isEqualTo(1); + assertThat(bridge.getLong("otel.instrumentation.example-instrumentation.int_key")) + .isEqualTo(1L); + assertThat(bridge.getDuration("otel.instrumentation.example-instrumentation.int_key")) + .isEqualTo(Duration.ofMillis(1)); + assertThat(bridge.getDouble("otel.instrumentation.example-instrumentation.double_key")) + .isEqualTo(1.1); + assertThat(bridge.getList("otel.instrumentation.example-instrumentation.list_key")) + .isEqualTo(Arrays.asList("value1", "value2")); + assertThat(bridge.getMap("otel.instrumentation.example-instrumentation.map_key")) + .isEqualTo(expectedMap); + + // asking for properties with the wrong type returns null + assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.string_key")) + .isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.bool_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.int_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.double_key")) + .isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.list_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.map_key")).isNull(); + + // check all the types + assertThat(bridge.getString("otel.instrumentation.other-instrumentation.string_key", "value")) + .isEqualTo("value"); + assertThat(bridge.getBoolean("otel.instrumentation.other-instrumentation.bool_key", true)) + .isTrue(); + assertThat(bridge.getInt("otel.instrumentation.other-instrumentation.int_key", 1)).isEqualTo(1); + assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L)) + .isEqualTo(1L); + assertThat( + bridge.getDuration( + "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) + .isEqualTo(Duration.ofMillis(1)); + assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1)) + .isEqualTo(1.1); + assertThat( + bridge.getList( + "otel.instrumentation.other-instrumentation.list_key", + Arrays.asList("value1", "value2"))) + .isEqualTo(Arrays.asList("value1", "value2")); + assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap)) + .isEqualTo(expectedMap); + + // verify vendor specific property names are preserved in unchanged form (prefix is not stripped + // as for otel.instrumentation.*) + assertThat(bridge.getBoolean("acme.full_name.preserved")).isTrue(); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 27db34c41..5c761f370 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,7 @@ include(":baggage-processor") include(":compressors:compressor-zstd") include(":cloudfoundry-resources") include(":consistent-sampling") +include(":declarative-config-bridge") include(":dependencyManagement") include(":disk-buffering") include(":example") From f6565c0a796b9b7fd1ed10397ef75c133d5def15 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 17:25:41 +0200 Subject: [PATCH 04/12] support config bridge --- gcp-auth-extension/build.gradle.kts | 3 + .../contrib/gcp/auth/ConfigurableOption.java | 22 ++- ...thAutoConfigurationCustomizerProvider.java | 41 +++--- .../gcp/auth/GcpAuthCustomizerProvider.java | 131 ++++++++---------- .../auth/GcpAuthCustomizerProviderTest.java | 70 ++++++++++ 5 files changed, 170 insertions(+), 97 deletions(-) create mode 100644 gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java diff --git a/gcp-auth-extension/build.gradle.kts b/gcp-auth-extension/build.gradle.kts index 0f2421b41..ec5a32ab5 100644 --- a/gcp-auth-extension/build.gradle.kts +++ b/gcp-auth-extension/build.gradle.kts @@ -14,6 +14,8 @@ val agent: Configuration by configurations.creating { } dependencies { + implementation(project(":declarative-config-bridge")) + annotationProcessor("com.google.auto.service:auto-service") // We use `compileOnly` dependency because during runtime all necessary classes are provided by // javaagent itself. @@ -36,6 +38,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-exporter-otlp") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations") testImplementation("org.awaitility:awaitility") diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java index 639207909..65c1c8e17 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java @@ -9,6 +9,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.util.Locale; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Supplier; /** @@ -99,9 +100,16 @@ String getUserReadableName() { * @throws ConfigurationException if neither the environment variable nor the system property is * set. */ - String getConfiguredValue(ConfigProperties configProperties) { - String configuredValue = configProperties.getString(this.getSystemProperty()); - if (configuredValue != null && !configuredValue.isEmpty()) { + T getConfiguredValue(ConfigProperties configProperties, BiFunction extractor) { + T configuredValue = extractor.apply(configProperties, this.getSystemProperty()); + if (configuredValue instanceof String) { + String value = (String) configuredValue; + if (value.isEmpty()) { + configuredValue = null; // Treat empty string as not configured + } + } + + if (configuredValue != null) { return configuredValue; } else { throw new ConfigurationException( @@ -121,10 +129,10 @@ String getConfiguredValue(ConfigProperties configProperties) { * @return The configured value for the option, obtained from the environment variable, system * property, or the fallback function, in that order of precedence. */ - String getConfiguredValueWithFallback( - ConfigProperties configProperties, Supplier fallback) { + T getConfiguredValueWithFallback( + ConfigProperties configProperties, Supplier fallback, BiFunction extractor) { try { - return this.getConfiguredValue(configProperties); + return this.getConfiguredValue(configProperties, extractor); } catch (ConfigurationException e) { return fallback.get(); } @@ -140,7 +148,7 @@ String getConfiguredValueWithFallback( */ Optional getConfiguredValueAsOptional(ConfigProperties configProperties) { try { - return Optional.of(this.getConfiguredValue(configProperties)); + return Optional.of(this.getConfiguredValue(configProperties, ConfigProperties::getString)); } catch (ConfigurationException e) { return Optional.empty(); } diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index b06359c38..ef6b40b52 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -25,7 +25,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.io.IOException; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -83,7 +83,7 @@ public class GcpAuthAutoConfigurationCustomizerProvider *
  • If the configured signal specific endpoint is a known GCP Telemetry API endpoint, * customizes only the signal specific exporter. * - * + *

    * The 'customization' performed includes customizing the exporters by adding required headers to * the export calls made and customizing the resource by adding required resource attributes to * enable GCP integration. @@ -151,17 +151,20 @@ private static MetricExporter customizeMetricExporter( } // Checks if the auth extension is configured to target the passed signal for authentication. - private static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) { - String userSpecifiedTargetedSignals = - ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValueWithFallback( - configProperties, () -> SIGNAL_TYPE_ALL); - return Arrays.stream(userSpecifiedTargetedSignals.split(",")) - .map(String::trim) - .anyMatch( + static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) { + return + targetSignals(configProperties).stream().anyMatch( targetedSignal -> targetedSignal.equals(checkSignal) || targetedSignal.equals(SIGNAL_TYPE_ALL)); } + static List targetSignals(ConfigProperties configProperties) { + return ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS + .getConfiguredValueWithFallback( + configProperties, () -> Collections.singletonList(SIGNAL_TYPE_ALL), + ConfigProperties::getList); + } + // Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and // OtlpHttpSpanExporter. private static SpanExporter addAuthorizationHeaders( @@ -221,23 +224,29 @@ static Map getRequiredHeaderMap( // Add quota user project header if not detected by the auth library and user provided it via // system properties. if (!flattenedHeaders.containsKey(QUOTA_USER_PROJECT_HEADER)) { - Optional maybeConfiguredQuotaProjectId = - ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValueAsOptional( - configProperties); - maybeConfiguredQuotaProjectId.ifPresent( + getQuotaProjectId(configProperties).ifPresent( configuredQuotaProjectId -> flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId)); } return flattenedHeaders; } + static Optional getQuotaProjectId(ConfigProperties configProperties) { + return ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValueAsOptional( + configProperties); + } + // Updates the current resource with the attributes required for ingesting OTLP data on GCP. private static Resource customizeResource(Resource resource, ConfigProperties configProperties) { - String gcpProjectId = - ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties); Resource res = Resource.create( - Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId)); + Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), + getProjectId(configProperties))); return resource.merge(res); } + + static String getProjectId(ConfigProperties configProperties) { + return ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties, + ConfigProperties::getString); + } } diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index e2512aab6..0aee50e95 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -5,15 +5,16 @@ package io.opentelemetry.contrib.gcp.auth; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.SIGNAL_TYPE_TRACES; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.isSignalTargeted; + import com.google.auth.oauth2.GoogleCredentials; import com.google.auto.service.AutoService; +import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessorModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordProcessorModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LoggerProviderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MeterProviderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel; @@ -23,7 +24,6 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpMetricExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleLogRecordProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; @@ -31,35 +31,60 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; @AutoService(DeclarativeConfigurationCustomizerProvider.class) public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { +// private static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION = +// String.format( +// "You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by exporting valid values to environment variable: %s or by setting valid values in system property: %s.", +// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getUserReadableName(), +// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getEnvironmentVariable(), +// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty()); + + + @Override public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { + ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model); GoogleCredentials credentials = GcpAuthAutoConfigurationCustomizerProvider.getCredentials(); - // todo pass config bridge - Map headerMap = - GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap(credentials, null); - customizeMeter(model, headerMap); - // todo are loggers supported now (not covered in old variant)? - customizeLogger(model, headerMap); - customizeTracer(model, headerMap); + customize(model, credentials, configProperties); return model; }); } - private void customizeMeter( - OpenTelemetryConfigurationModel model, Map headerMap) { + static void customize(OpenTelemetryConfigurationModel model, + GoogleCredentials credentials, ConfigProperties configProperties) { + Map headerMap = + GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap(credentials, + configProperties); + customizeMeter(model, headerMap, configProperties); + customizeTracer(model, headerMap, configProperties); + } + + private static void customizeMeter( + OpenTelemetryConfigurationModel model, Map headerMap, + ConfigProperties configProperties) { MeterProviderModel meterProvider = model.getMeterProvider(); if (meterProvider == null) { return; } + if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { + // todo + // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; + // logger.log( + // Level.WARNING, + // "GCP Authentication Extension is not configured for signal type: {0}. {1}", + // params); + return; + } + for (MetricReaderModel reader : meterProvider.getReaders()) { if (reader.getPeriodic() != null) { addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); @@ -67,7 +92,8 @@ private void customizeMeter( } } - private List> meterModelHeaders(PushMetricExporterModel exporter) { + private static List> meterModelHeaders( + @Nullable PushMetricExporterModel exporter) { ArrayList> list = new ArrayList<>(); if (exporter == null) { return list; @@ -83,62 +109,23 @@ private List> meterModelHeaders(PushMetricExporte return list; } - private void customizeLogger( - OpenTelemetryConfigurationModel model, Map headerMap) { - LoggerProviderModel loggerProvider = model.getLoggerProvider(); - if (loggerProvider == null) { - return; - } - for (LogRecordProcessorModel processor : loggerProvider.getProcessors()) { - BatchLogRecordProcessorModel batch = processor.getBatch(); - if (batch != null) { - addAuth(logRecordModelHeaders(batch.getExporter()), headerMap); - } - SimpleLogRecordProcessorModel simple = processor.getSimple(); - if (simple != null) { - addAuth(logRecordModelHeaders(simple.getExporter()), headerMap); - } - } - } - - private List> logRecordModelHeaders( - LogRecordExporterModel exporter) { - ArrayList> list = new ArrayList<>(); - - if (exporter == null) { - return list; - } - OtlpGrpcExporterModel grpc = exporter.getOtlpGrpc(); - if (grpc != null) { - list.add(grpc.getHeaders()); - } - OtlpHttpExporterModel http = exporter.getOtlpHttp(); - if (http != null) { - list.add(http.getHeaders()); - } - return list; - } - - private void customizeTracer( - OpenTelemetryConfigurationModel model, Map headerMap) { + private static void customizeTracer( + OpenTelemetryConfigurationModel model, Map headerMap, + ConfigProperties configProperties) { TracerProviderModel tracerProvider = model.getTracerProvider(); if (tracerProvider == null) { return; } - // todo here we would want a simplified version of the declarative config bridge - // https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java - // googleNode(model) - - // if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { - // todo - // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; - // logger.log( - // Level.WARNING, - // "GCP Authentication Extension is not configured for signal type: {0}. {1}", - // params); - // return; - // } + if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { + // todo + // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; + // logger.log( + // Level.WARNING, + // "GCP Authentication Extension is not configured for signal type: {0}. {1}", + // params); + return; + } for (SpanProcessorModel processor : tracerProvider.getProcessors()) { BatchSpanProcessorModel batch = processor.getBatch(); @@ -152,12 +139,8 @@ private void customizeTracer( } } - private void googleNode(OpenTelemetryConfigurationModel model) { - // todo use declarative config bridge - } - - private List> spanExporterModelHeaders( - SpanExporterModel exporter) { + private static List> spanExporterModelHeaders( + @Nullable SpanExporterModel exporter) { ArrayList> list = new ArrayList<>(); if (exporter == null) { @@ -174,12 +157,12 @@ private List> spanExporterModelHeaders( return list; } - private void addAuth( + private static void addAuth( List> headerConsumers, Map headerMap) { headerConsumers.forEach(headers -> addHeaders(headers, headerMap)); } - private void addHeaders(List headers, Map add) { + private static void addHeaders(List headers, Map add) { add.forEach( (key, value) -> { if (headers.stream().noneMatch(header -> key.equals(header.getName()))) { diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java new file mode 100644 index 000000000..5d7fe46e8 --- /dev/null +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java @@ -0,0 +1,70 @@ +package io.opentelemetry.contrib.gcp.auth; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.auth.oauth2.GoogleCredentials; +import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +class GcpAuthCustomizerProviderTest { + + @Test + void declarativeConfig() throws IOException { + String yaml = + "file_format: 0.4\n" + + "tracer_provider:\n" + + " processors:\n" + + " - simple:\n" + + " exporter:\n" + + " otlp_http:\n" + + "meter_provider:\n" + + " readers:\n" + + " - periodic:\n" + + " exporter:\n" + + " otlp_http:\n" + + "instrumentation/development:\n" + + " java:\n" + + " google:\n" + + " cloud:\n" + + " project: p\n" + + " quota:\n" + + " project: qp\n" + + " otel:\n" + + " auth:\n" + + " target:\n" + + " signals: [metrics, traces]\n"; + + OpenTelemetryConfigurationModel model = DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + ConfigProperties properties = ConfigPropertiesUtil.resolveModel(model); + + assertThat( + GcpAuthAutoConfigurationCustomizerProvider.targetSignals(properties)).containsExactly( + "metrics", "traces" + ); + assertThat(GcpAuthAutoConfigurationCustomizerProvider.getProjectId(properties)).isEqualTo( + "p"); + assertThat(GcpAuthAutoConfigurationCustomizerProvider.getQuotaProjectId(properties)).contains( + "qp"); + + GoogleCredentials credentials = mock(GoogleCredentials.class); + when(credentials.getRequestMetadata()) + .thenReturn( + Collections.singletonMap("x-goog-user-project", Collections.singletonList("qp"))); + + GcpAuthCustomizerProvider.customize(model, credentials, properties); + + String header = "headers=\\[io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel@.*\\[name=x-goog-user-project,value=qp]"; + // both metrics and traces should have the header + assertThat(model.toString()).matches(String.format(".*%s.*%s.*", header, header)); + } +} From 7c56e9cf3a3063f137398b6afb284fcf0ad3caa7 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:07:01 +0200 Subject: [PATCH 05/12] support config bridge --- .../autoconfigure/ConfigPropertiesUtil.java | 5 + .../DeclarativeConfigPropertiesBridge.java | 207 ++++++++++-------- ...thAutoConfigurationCustomizerProvider.java | 28 ++- .../gcp/auth/GcpAuthCustomizerProvider.java | 65 +++--- .../auth/GcpAuthCustomizerProviderTest.java | 11 + 5 files changed, 168 insertions(+), 148 deletions(-) diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java index af6cc6397..140a43297 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -52,4 +52,9 @@ public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel mode return new DeclarativeConfigPropertiesBridge(instrumentationConfig); } + + public static String propertyYamlPath(String propertyName) { + // todo test if this is correct + return DeclarativeConfigPropertiesBridge.yamlPath(propertyName); + } } diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java index 0721e4a69..c1d835bf5 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -49,110 +49,123 @@ */ final class DeclarativeConfigPropertiesBridge implements ConfigProperties { - private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; - - // The node at .instrumentation.java - private final DeclarativeConfigProperties instrumentationJavaNode; - - DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { - instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); - } - - @Nullable - @Override - public String getString(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getString); - } - - @Nullable - @Override - public Boolean getBoolean(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean); - } - - @Nullable - @Override - public Integer getInt(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt); - } - - @Nullable - @Override - public Long getLong(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); - } - - @Nullable - @Override - public Double getDouble(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble); - } - - @Nullable - @Override - public Duration getDuration(String propertyName) { - Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); - if (millis == null) { - return null; + private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; + + // The node at .instrumentation.java + private final DeclarativeConfigProperties instrumentationJavaNode; + + DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { + instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); } - return Duration.ofMillis(millis); - } - - @Override - public List getList(String propertyName) { - List propertyValue = - getPropertyValue( - propertyName, - (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); - return propertyValue == null ? Collections.emptyList() : propertyValue; - } - - @Override - public Map getMap(String propertyName) { - DeclarativeConfigProperties propertyValue = - getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured); - if (propertyValue == null) { - return Collections.emptyMap(); + + @Nullable + @Override + public String getString(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getString); } - Map result = new HashMap<>(); - propertyValue - .getPropertyKeys() - .forEach( - key -> { - String value = propertyValue.getString(key); - if (value == null) { - return; - } - result.put(key, value); - }); - return Collections.unmodifiableMap(result); - } - - @Nullable - private T getPropertyValue( - String property, BiFunction extractor) { - if (instrumentationJavaNode == null) { - return null; + + @Nullable + @Override + public Boolean getBoolean(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean); } - if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { - property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); + @Nullable + @Override + public Integer getInt(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt); } - // Split the remainder of the property on "." - String[] segments = property.split("\\."); - if (segments.length == 0) { - return null; + + @Nullable + @Override + public Long getLong(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + } + + @Nullable + @Override + public Double getDouble(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble); + } + + @Nullable + @Override + public Duration getDuration(String propertyName) { + Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + if (millis == null) { + return null; + } + return Duration.ofMillis(millis); } - // Extract the value by walking to the N-1 entry - DeclarativeConfigProperties target = instrumentationJavaNode; - if (segments.length > 1) { - for (int i = 0; i < segments.length - 1; i++) { - target = target.getStructured(segments[i], empty()); - } + @Override + public List getList(String propertyName) { + List propertyValue = + getPropertyValue( + propertyName, + (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); + return propertyValue == null ? Collections.emptyList() : propertyValue; } - String lastPart = segments[segments.length - 1]; - return extractor.apply(target, lastPart); - } + @Override + public Map getMap(String propertyName) { + DeclarativeConfigProperties propertyValue = + getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured); + if (propertyValue == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + propertyValue + .getPropertyKeys() + .forEach( + key -> { + String value = propertyValue.getString(key); + if (value == null) { + return; + } + result.put(key, value); + }); + return Collections.unmodifiableMap(result); + } + + @Nullable + private T getPropertyValue( + String property, BiFunction extractor) { + if (instrumentationJavaNode == null) { + return null; + } + + String[] segments = getSegments(property); + if (segments.length == 0) { + return null; + } + + // Extract the value by walking to the N-1 entry + DeclarativeConfigProperties target = instrumentationJavaNode; + if (segments.length > 1) { + for (int i = 0; i < segments.length - 1; i++) { + target = target.getStructured(segments[i], empty()); + } + } + String lastPart = segments[segments.length - 1]; + + return extractor.apply(target, lastPart); + } + + private static String[] getSegments(String property) { + if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { + property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); + } + // Split the remainder of the property on "." + return property.split("\\."); + } + + static String yamlPath(String property) { + String[] segments = getSegments(property); + if (segments.length == 0) { + throw new IllegalArgumentException("Invalid property: " + property); + } + + return "'instrumentation/development' / 'java' / '" + String.join("' / '", segments) + "'"; + } } diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index ef6b40b52..ed096e1d8 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -124,34 +124,38 @@ public int order() { private static SpanExporter customizeSpanExporter( SpanExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) { - if (isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { + if (shouldConfigureExporter(SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, + configProperties)) { return addAuthorizationHeaders(exporter, credentials, configProperties); - } else { - String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; - logger.log( - Level.WARNING, - "GCP Authentication Extension is not configured for signal type: {0}. {1}", - params); } return exporter; } private static MetricExporter customizeMetricExporter( MetricExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) { - if (isSignalTargeted(SIGNAL_TYPE_METRICS, configProperties)) { + if (shouldConfigureExporter(SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, + configProperties)) { return addAuthorizationHeaders(exporter, credentials, configProperties); + } + return exporter; + } + + static boolean shouldConfigureExporter(String signal, + String fixSuggestion, + ConfigProperties configProperties) { + if (isSignalTargeted(signal, configProperties)) { + return true; } else { - String[] params = {SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; logger.log( Level.WARNING, "GCP Authentication Extension is not configured for signal type: {0}. {1}", - params); + new String[] {signal, fixSuggestion}); + return false; } - return exporter; } // Checks if the auth extension is configured to target the passed signal for authentication. - static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) { + private static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) { return targetSignals(configProperties).stream().anyMatch( targetedSignal -> diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index 0aee50e95..7ced14552 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -5,8 +5,9 @@ package io.opentelemetry.contrib.gcp.auth; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.SIGNAL_TYPE_METRICS; import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.SIGNAL_TYPE_TRACES; -import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.isSignalTargeted; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.shouldConfigureExporter; import com.google.auth.oauth2.GoogleCredentials; import com.google.auto.service.AutoService; @@ -36,13 +37,12 @@ @AutoService(DeclarativeConfigurationCustomizerProvider.class) public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { -// private static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION = -// String.format( -// "You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by exporting valid values to environment variable: %s or by setting valid values in system property: %s.", -// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getUserReadableName(), -// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getEnvironmentVariable(), -// ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty()); - + static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION = + String.format( + "You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by setting %s in the configuration file.", + ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getUserReadableName(), + ConfigPropertiesUtil.propertyYamlPath( + ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty())); @Override @@ -75,19 +75,12 @@ private static void customizeMeter( return; } - if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { - // todo - // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; - // logger.log( - // Level.WARNING, - // "GCP Authentication Extension is not configured for signal type: {0}. {1}", - // params); - return; - } - - for (MetricReaderModel reader : meterProvider.getReaders()) { - if (reader.getPeriodic() != null) { - addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); + if (shouldConfigureExporter(SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, + configProperties)) { + for (MetricReaderModel reader : meterProvider.getReaders()) { + if (reader.getPeriodic() != null) { + addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); + } } } } @@ -117,26 +110,20 @@ private static void customizeTracer( return; } - if (!isSignalTargeted(SIGNAL_TYPE_TRACES, configProperties)) { - // todo - // String[] params = {SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION}; - // logger.log( - // Level.WARNING, - // "GCP Authentication Extension is not configured for signal type: {0}. {1}", - // params); - return; - } - - for (SpanProcessorModel processor : tracerProvider.getProcessors()) { - BatchSpanProcessorModel batch = processor.getBatch(); - if (batch != null) { - addAuth(spanExporterModelHeaders(batch.getExporter()), headerMap); - } - SimpleSpanProcessorModel simple = processor.getSimple(); - if (simple != null) { - addAuth(spanExporterModelHeaders(simple.getExporter()), headerMap); + if (shouldConfigureExporter(SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, + configProperties)) { + for (SpanProcessorModel processor : tracerProvider.getProcessors()) { + BatchSpanProcessorModel batch = processor.getBatch(); + if (batch != null) { + addAuth(spanExporterModelHeaders(batch.getExporter()), headerMap); + } + SimpleSpanProcessorModel simple = processor.getSimple(); + if (simple != null) { + addAuth(spanExporterModelHeaders(simple.getExporter()), headerMap); + } } } + } private static List> spanExporterModelHeaders( diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java index 5d7fe46e8..171c1825f 100644 --- a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java @@ -1,5 +1,6 @@ package io.opentelemetry.contrib.gcp.auth; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthCustomizerProvider.SIGNAL_TARGET_WARNING_FIX_SUGGESTION; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -67,4 +68,14 @@ void declarativeConfig() throws IOException { // both metrics and traces should have the header assertThat(model.toString()).matches(String.format(".*%s.*%s.*", header, header)); } + + @Test + void fixSuggestion() { + assertThat(SIGNAL_TARGET_WARNING_FIX_SUGGESTION) + .isEqualTo("You may safely ignore this warning if it is intentional, " + + "otherwise please configure the 'Target Signals for Google Authentication Extension' " + + "by setting " + + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / " + + "'signals' in the configuration file."); + } } From a7d4bf8f17f369cde62a134f2944e0756d513fdb Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:15:47 +0200 Subject: [PATCH 06/12] copy declarative config bridge from instrumentation --- .../contrib/sdk/autoconfigure/ConfigPropertiesUtil.java | 1 - .../sdk/autoconfigure/ConfigPropertiesUtilTest.java | 7 +++++++ .../DeclarativeConfigPropertiesBridgeTest.java | 8 ++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java index 140a43297..ee71ccaad 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -54,7 +54,6 @@ public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel mode } public static String propertyYamlPath(String propertyName) { - // todo test if this is correct return DeclarativeConfigPropertiesBridge.yamlPath(propertyName); } } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java index 8d88e9783..5d74a24a8 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.contrib.sdk.autoconfigure; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -84,4 +85,10 @@ void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig( assertThat(configProperties.getString("testProperty")).isEqualTo(null); } } + + @Test + void propertyYamlPath() { + assertThat(ConfigPropertiesUtil.propertyYamlPath("google.otel.auth.target.signals")) + .isEqualTo("'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / 'signals'"); + } } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java index 5bff96e16..34b3a7036 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -3,7 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import java.io.ByteArrayInputStream; @@ -53,7 +57,7 @@ void setup() { new ByteArrayInputStream(YAML.getBytes(StandardCharsets.UTF_8))); SdkConfigProvider configProvider = SdkConfigProvider.create(model); bridge = - new ConfigPropertiesUtil.DeclarativeConfigPropertiesBridge( + new DeclarativeConfigPropertiesBridge( Objects.requireNonNull(configProvider.getInstrumentationConfig())); OpenTelemetryConfigurationModel emptyModel = @@ -61,7 +65,7 @@ void setup() { .withAdditionalProperty("instrumentation/development", new InstrumentationModel()); SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel); emptyBridge = - new ConfigPropertiesUtil.DeclarativeConfigPropertiesBridge( + new DeclarativeConfigPropertiesBridge( Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig())); } From 5ef081166b621ba01cc19ddc2711b20cc40b9509 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:16:20 +0200 Subject: [PATCH 07/12] format --- .../autoconfigure/ConfigPropertiesUtil.java | 71 +++--- .../DeclarativeConfigPropertiesBridge.java | 221 +++++++++--------- .../ConfigPropertiesUtilTest.java | 4 +- ...DeclarativeConfigPropertiesBridgeTest.java | 7 +- .../contrib/gcp/auth/ConfigurableOption.java | 7 +- ...thAutoConfigurationCustomizerProvider.java | 46 ++-- .../gcp/auth/GcpAuthCustomizerProvider.java | 26 ++- .../auth/GcpAuthCustomizerProviderTest.java | 37 +-- 8 files changed, 215 insertions(+), 204 deletions(-) diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java index ee71ccaad..63db139f9 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -14,46 +14,43 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; public class ConfigPropertiesUtil { - private ConfigPropertiesUtil() { + private ConfigPropertiesUtil() {} + + /** Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */ + public static ConfigProperties resolveConfigProperties( + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + ConfigProperties sdkConfigProperties = + AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); + if (sdkConfigProperties != null) { + return sdkConfigProperties; } + ConfigProvider configProvider = + AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk); + if (configProvider != null) { + DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); - /** - * Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. - */ - public static ConfigProperties resolveConfigProperties( - AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - ConfigProperties sdkConfigProperties = - AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); - if (sdkConfigProperties != null) { - return sdkConfigProperties; - } - ConfigProvider configProvider = - AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk); - if (configProvider != null) { - DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); - - if (instrumentationConfig == null) { - instrumentationConfig = DeclarativeConfigProperties.empty(); - } - - return new DeclarativeConfigPropertiesBridge(instrumentationConfig); - } - // Should never happen - throw new IllegalStateException( - "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); - } - - public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model) { - SdkConfigProvider configProvider = SdkConfigProvider.create(model); - DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); - if (instrumentationConfig == null) { - instrumentationConfig = DeclarativeConfigProperties.empty(); - } + if (instrumentationConfig == null) { + instrumentationConfig = DeclarativeConfigProperties.empty(); + } - return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + return new DeclarativeConfigPropertiesBridge(instrumentationConfig); } - - public static String propertyYamlPath(String propertyName) { - return DeclarativeConfigPropertiesBridge.yamlPath(propertyName); + // Should never happen + throw new IllegalStateException( + "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); + } + + public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model) { + SdkConfigProvider configProvider = SdkConfigProvider.create(model); + DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); + if (instrumentationConfig == null) { + instrumentationConfig = DeclarativeConfigProperties.empty(); } + + return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + } + + public static String propertyYamlPath(String propertyName) { + return DeclarativeConfigPropertiesBridge.yamlPath(propertyName); + } } diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java index c1d835bf5..47173f7ac 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -5,18 +5,17 @@ package io.opentelemetry.contrib.sdk.autoconfigure; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; - -import javax.annotation.Nullable; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiFunction; - -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; +import javax.annotation.Nullable; /** * A {@link ConfigProperties} which resolves properties based on {@link @@ -49,123 +48,123 @@ */ final class DeclarativeConfigPropertiesBridge implements ConfigProperties { - private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; - - // The node at .instrumentation.java - private final DeclarativeConfigProperties instrumentationJavaNode; - - DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { - instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); + private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; + + // The node at .instrumentation.java + private final DeclarativeConfigProperties instrumentationJavaNode; + + DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { + instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); + } + + @Nullable + @Override + public String getString(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getString); + } + + @Nullable + @Override + public Boolean getBoolean(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean); + } + + @Nullable + @Override + public Integer getInt(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt); + } + + @Nullable + @Override + public Long getLong(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + } + + @Nullable + @Override + public Double getDouble(String propertyName) { + return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble); + } + + @Nullable + @Override + public Duration getDuration(String propertyName) { + Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + if (millis == null) { + return null; } - - @Nullable - @Override - public String getString(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getString); + return Duration.ofMillis(millis); + } + + @Override + public List getList(String propertyName) { + List propertyValue = + getPropertyValue( + propertyName, + (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); + return propertyValue == null ? Collections.emptyList() : propertyValue; + } + + @Override + public Map getMap(String propertyName) { + DeclarativeConfigProperties propertyValue = + getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured); + if (propertyValue == null) { + return Collections.emptyMap(); } - - @Nullable - @Override - public Boolean getBoolean(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean); + Map result = new HashMap<>(); + propertyValue + .getPropertyKeys() + .forEach( + key -> { + String value = propertyValue.getString(key); + if (value == null) { + return; + } + result.put(key, value); + }); + return Collections.unmodifiableMap(result); + } + + @Nullable + private T getPropertyValue( + String property, BiFunction extractor) { + if (instrumentationJavaNode == null) { + return null; } - @Nullable - @Override - public Integer getInt(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt); + String[] segments = getSegments(property); + if (segments.length == 0) { + return null; } - @Nullable - @Override - public Long getLong(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); + // Extract the value by walking to the N-1 entry + DeclarativeConfigProperties target = instrumentationJavaNode; + if (segments.length > 1) { + for (int i = 0; i < segments.length - 1; i++) { + target = target.getStructured(segments[i], empty()); + } } + String lastPart = segments[segments.length - 1]; - @Nullable - @Override - public Double getDouble(String propertyName) { - return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble); - } - - @Nullable - @Override - public Duration getDuration(String propertyName) { - Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong); - if (millis == null) { - return null; - } - return Duration.ofMillis(millis); - } - - @Override - public List getList(String propertyName) { - List propertyValue = - getPropertyValue( - propertyName, - (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); - return propertyValue == null ? Collections.emptyList() : propertyValue; - } + return extractor.apply(target, lastPart); + } - @Override - public Map getMap(String propertyName) { - DeclarativeConfigProperties propertyValue = - getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured); - if (propertyValue == null) { - return Collections.emptyMap(); - } - Map result = new HashMap<>(); - propertyValue - .getPropertyKeys() - .forEach( - key -> { - String value = propertyValue.getString(key); - if (value == null) { - return; - } - result.put(key, value); - }); - return Collections.unmodifiableMap(result); + private static String[] getSegments(String property) { + if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { + property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); } - - @Nullable - private T getPropertyValue( - String property, BiFunction extractor) { - if (instrumentationJavaNode == null) { - return null; - } - - String[] segments = getSegments(property); - if (segments.length == 0) { - return null; - } - - // Extract the value by walking to the N-1 entry - DeclarativeConfigProperties target = instrumentationJavaNode; - if (segments.length > 1) { - for (int i = 0; i < segments.length - 1; i++) { - target = target.getStructured(segments[i], empty()); - } - } - String lastPart = segments[segments.length - 1]; - - return extractor.apply(target, lastPart); - } - - private static String[] getSegments(String property) { - if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { - property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); - } - // Split the remainder of the property on "." - return property.split("\\."); + // Split the remainder of the property on "." + return property.split("\\."); + } + + static String yamlPath(String property) { + String[] segments = getSegments(property); + if (segments.length == 0) { + throw new IllegalArgumentException("Invalid property: " + property); } - static String yamlPath(String property) { - String[] segments = getSegments(property); - if (segments.length == 0) { - throw new IllegalArgumentException("Invalid property: " + property); - } - - return "'instrumentation/development' / 'java' / '" + String.join("' / '", segments) + "'"; - } + return "'instrumentation/development' / 'java' / '" + String.join("' / '", segments) + "'"; + } } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java index 5d74a24a8..9981ffdb2 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtilTest.java @@ -5,7 +5,6 @@ package io.opentelemetry.contrib.sdk.autoconfigure; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -89,6 +88,7 @@ void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig( @Test void propertyYamlPath() { assertThat(ConfigPropertiesUtil.propertyYamlPath("google.otel.auth.target.signals")) - .isEqualTo("'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / 'signals'"); + .isEqualTo( + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / 'signals'"); } } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java index 34b3a7036..caadda60b 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -1,8 +1,13 @@ -package io.opentelemetry.contrib.sdk.autoconfigure;/* +/* * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ +package io.opentelemetry.contrib.sdk.autoconfigure; /* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java index 65c1c8e17..5756d1224 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java @@ -100,7 +100,8 @@ String getUserReadableName() { * @throws ConfigurationException if neither the environment variable nor the system property is * set. */ - T getConfiguredValue(ConfigProperties configProperties, BiFunction extractor) { + T getConfiguredValue( + ConfigProperties configProperties, BiFunction extractor) { T configuredValue = extractor.apply(configProperties, this.getSystemProperty()); if (configuredValue instanceof String) { String value = (String) configuredValue; @@ -130,7 +131,9 @@ T getConfiguredValue(ConfigProperties configProperties, BiFunction T getConfiguredValueWithFallback( - ConfigProperties configProperties, Supplier fallback, BiFunction extractor) { + ConfigProperties configProperties, + Supplier fallback, + BiFunction extractor) { try { return this.getConfiguredValue(configProperties, extractor); } catch (ConfigurationException e) { diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index ed096e1d8..a54dd677d 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -83,9 +83,9 @@ public class GcpAuthAutoConfigurationCustomizerProvider *

  • If the configured signal specific endpoint is a known GCP Telemetry API endpoint, * customizes only the signal specific exporter. * - *

    - * The 'customization' performed includes customizing the exporters by adding required headers to - * the export calls made and customizing the resource by adding required resource attributes to + * + *

    The 'customization' performed includes customizing the exporters by adding required headers + * to the export calls made and customizing the resource by adding required resource attributes to * enable GCP integration. * * @param autoConfiguration the AutoConfigurationCustomizer to customize. @@ -124,8 +124,8 @@ public int order() { private static SpanExporter customizeSpanExporter( SpanExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) { - if (shouldConfigureExporter(SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, - configProperties)) { + if (shouldConfigureExporter( + SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { return addAuthorizationHeaders(exporter, credentials, configProperties); } return exporter; @@ -133,16 +133,15 @@ private static SpanExporter customizeSpanExporter( private static MetricExporter customizeMetricExporter( MetricExporter exporter, GoogleCredentials credentials, ConfigProperties configProperties) { - if (shouldConfigureExporter(SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, - configProperties)) { + if (shouldConfigureExporter( + SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { return addAuthorizationHeaders(exporter, credentials, configProperties); } return exporter; } - static boolean shouldConfigureExporter(String signal, - String fixSuggestion, - ConfigProperties configProperties) { + static boolean shouldConfigureExporter( + String signal, String fixSuggestion, ConfigProperties configProperties) { if (isSignalTargeted(signal, configProperties)) { return true; } else { @@ -156,17 +155,17 @@ static boolean shouldConfigureExporter(String signal, // Checks if the auth extension is configured to target the passed signal for authentication. private static boolean isSignalTargeted(String checkSignal, ConfigProperties configProperties) { - return - targetSignals(configProperties).stream().anyMatch( + return targetSignals(configProperties).stream() + .anyMatch( targetedSignal -> targetedSignal.equals(checkSignal) || targetedSignal.equals(SIGNAL_TYPE_ALL)); } static List targetSignals(ConfigProperties configProperties) { - return ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS - .getConfiguredValueWithFallback( - configProperties, () -> Collections.singletonList(SIGNAL_TYPE_ALL), - ConfigProperties::getList); + return ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValueWithFallback( + configProperties, + () -> Collections.singletonList(SIGNAL_TYPE_ALL), + ConfigProperties::getList); } // Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and @@ -228,9 +227,10 @@ static Map getRequiredHeaderMap( // Add quota user project header if not detected by the auth library and user provided it via // system properties. if (!flattenedHeaders.containsKey(QUOTA_USER_PROJECT_HEADER)) { - getQuotaProjectId(configProperties).ifPresent( - configuredQuotaProjectId -> - flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId)); + getQuotaProjectId(configProperties) + .ifPresent( + configuredQuotaProjectId -> + flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId)); } return flattenedHeaders; } @@ -244,13 +244,13 @@ static Optional getQuotaProjectId(ConfigProperties configProperties) { private static Resource customizeResource(Resource resource, ConfigProperties configProperties) { Resource res = Resource.create( - Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), - getProjectId(configProperties))); + Attributes.of( + AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), getProjectId(configProperties))); return resource.merge(res); } static String getProjectId(ConfigProperties configProperties) { - return ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue(configProperties, - ConfigProperties::getString); + return ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue( + configProperties, ConfigProperties::getString); } } diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index 7ced14552..90a3d17f2 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -44,7 +44,6 @@ public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustom ConfigPropertiesUtil.propertyYamlPath( ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getSystemProperty())); - @Override public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( @@ -58,25 +57,28 @@ public void customize(DeclarativeConfigurationCustomizer customizer) { }); } - static void customize(OpenTelemetryConfigurationModel model, - GoogleCredentials credentials, ConfigProperties configProperties) { + static void customize( + OpenTelemetryConfigurationModel model, + GoogleCredentials credentials, + ConfigProperties configProperties) { Map headerMap = - GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap(credentials, - configProperties); + GcpAuthAutoConfigurationCustomizerProvider.getRequiredHeaderMap( + credentials, configProperties); customizeMeter(model, headerMap, configProperties); customizeTracer(model, headerMap, configProperties); } private static void customizeMeter( - OpenTelemetryConfigurationModel model, Map headerMap, + OpenTelemetryConfigurationModel model, + Map headerMap, ConfigProperties configProperties) { MeterProviderModel meterProvider = model.getMeterProvider(); if (meterProvider == null) { return; } - if (shouldConfigureExporter(SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, - configProperties)) { + if (shouldConfigureExporter( + SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { for (MetricReaderModel reader : meterProvider.getReaders()) { if (reader.getPeriodic() != null) { addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); @@ -103,15 +105,16 @@ private static List> meterModelHeaders( } private static void customizeTracer( - OpenTelemetryConfigurationModel model, Map headerMap, + OpenTelemetryConfigurationModel model, + Map headerMap, ConfigProperties configProperties) { TracerProviderModel tracerProvider = model.getTracerProvider(); if (tracerProvider == null) { return; } - if (shouldConfigureExporter(SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, - configProperties)) { + if (shouldConfigureExporter( + SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { for (SpanProcessorModel processor : tracerProvider.getProcessors()) { BatchSpanProcessorModel batch = processor.getBatch(); if (batch != null) { @@ -123,7 +126,6 @@ private static void customizeTracer( } } } - } private static List> spanExporterModelHeaders( diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java index 171c1825f..9ccff6129 100644 --- a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java @@ -1,3 +1,8 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + package io.opentelemetry.contrib.gcp.auth; import static io.opentelemetry.contrib.gcp.auth.GcpAuthCustomizerProvider.SIGNAL_TARGET_WARNING_FIX_SUGGESTION; @@ -44,18 +49,16 @@ void declarativeConfig() throws IOException { + " target:\n" + " signals: [metrics, traces]\n"; - OpenTelemetryConfigurationModel model = DeclarativeConfiguration.parse( - new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); ConfigProperties properties = ConfigPropertiesUtil.resolveModel(model); - assertThat( - GcpAuthAutoConfigurationCustomizerProvider.targetSignals(properties)).containsExactly( - "metrics", "traces" - ); - assertThat(GcpAuthAutoConfigurationCustomizerProvider.getProjectId(properties)).isEqualTo( - "p"); - assertThat(GcpAuthAutoConfigurationCustomizerProvider.getQuotaProjectId(properties)).contains( - "qp"); + assertThat(GcpAuthAutoConfigurationCustomizerProvider.targetSignals(properties)) + .containsExactly("metrics", "traces"); + assertThat(GcpAuthAutoConfigurationCustomizerProvider.getProjectId(properties)).isEqualTo("p"); + assertThat(GcpAuthAutoConfigurationCustomizerProvider.getQuotaProjectId(properties)) + .contains("qp"); GoogleCredentials credentials = mock(GoogleCredentials.class); when(credentials.getRequestMetadata()) @@ -64,7 +67,8 @@ void declarativeConfig() throws IOException { GcpAuthCustomizerProvider.customize(model, credentials, properties); - String header = "headers=\\[io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel@.*\\[name=x-goog-user-project,value=qp]"; + String header = + "headers=\\[io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel@.*\\[name=x-goog-user-project,value=qp]"; // both metrics and traces should have the header assertThat(model.toString()).matches(String.format(".*%s.*%s.*", header, header)); } @@ -72,10 +76,11 @@ void declarativeConfig() throws IOException { @Test void fixSuggestion() { assertThat(SIGNAL_TARGET_WARNING_FIX_SUGGESTION) - .isEqualTo("You may safely ignore this warning if it is intentional, " - + "otherwise please configure the 'Target Signals for Google Authentication Extension' " - + "by setting " - + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / " - + "'signals' in the configuration file."); + .isEqualTo( + "You may safely ignore this warning if it is intentional, " + + "otherwise please configure the 'Target Signals for Google Authentication Extension' " + + "by setting " + + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / " + + "'signals' in the configuration file."); } } From a03ad5982fbad9484fb02fc91004927d4512ce4a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:21:06 +0200 Subject: [PATCH 08/12] cleanup --- .../contrib/gcp/auth/GcpAuthCustomizerProvider.java | 12 +++++------- .../gcp/auth/GcpAuthCustomizerProviderTest.java | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index 90a3d17f2..1b71800e4 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -37,7 +37,7 @@ @AutoService(DeclarativeConfigurationCustomizerProvider.class) public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { - static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION = + static final String SIGNAL_TARGET_WARNING_YAML_FIX_SUGGESTION = String.format( "You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by setting %s in the configuration file.", ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getUserReadableName(), @@ -48,10 +48,8 @@ public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustom public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { - ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model); - GoogleCredentials credentials = - GcpAuthAutoConfigurationCustomizerProvider.getCredentials(); - customize(model, credentials, configProperties); + customize(model, GcpAuthAutoConfigurationCustomizerProvider.getCredentials(), + ConfigPropertiesUtil.resolveModel(model)); return model; }); @@ -78,7 +76,7 @@ private static void customizeMeter( } if (shouldConfigureExporter( - SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { + SIGNAL_TYPE_METRICS, SIGNAL_TARGET_WARNING_YAML_FIX_SUGGESTION, configProperties)) { for (MetricReaderModel reader : meterProvider.getReaders()) { if (reader.getPeriodic() != null) { addAuth(meterModelHeaders(reader.getPeriodic().getExporter()), headerMap); @@ -114,7 +112,7 @@ private static void customizeTracer( } if (shouldConfigureExporter( - SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_FIX_SUGGESTION, configProperties)) { + SIGNAL_TYPE_TRACES, SIGNAL_TARGET_WARNING_YAML_FIX_SUGGESTION, configProperties)) { for (SpanProcessorModel processor : tracerProvider.getProcessors()) { BatchSpanProcessorModel batch = processor.getBatch(); if (batch != null) { diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java index 9ccff6129..fd469eae0 100644 --- a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProviderTest.java @@ -5,7 +5,7 @@ package io.opentelemetry.contrib.gcp.auth; -import static io.opentelemetry.contrib.gcp.auth.GcpAuthCustomizerProvider.SIGNAL_TARGET_WARNING_FIX_SUGGESTION; +import static io.opentelemetry.contrib.gcp.auth.GcpAuthCustomizerProvider.SIGNAL_TARGET_WARNING_YAML_FIX_SUGGESTION; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -75,7 +75,7 @@ void declarativeConfig() throws IOException { @Test void fixSuggestion() { - assertThat(SIGNAL_TARGET_WARNING_FIX_SUGGESTION) + assertThat(SIGNAL_TARGET_WARNING_YAML_FIX_SUGGESTION) .isEqualTo( "You may safely ignore this warning if it is intentional, " + "otherwise please configure the 'Target Signals for Google Authentication Extension' " From 1d5e256ff140c70120edbf9c84c45b9f0bec7294 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:22:04 +0200 Subject: [PATCH 09/12] cleanup --- .../contrib/gcp/auth/GcpAuthCustomizerProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java index 1b71800e4..297a47596 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthCustomizerProvider.java @@ -48,7 +48,9 @@ public class GcpAuthCustomizerProvider implements DeclarativeConfigurationCustom public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { - customize(model, GcpAuthAutoConfigurationCustomizerProvider.getCredentials(), + customize( + model, + GcpAuthAutoConfigurationCustomizerProvider.getCredentials(), ConfigPropertiesUtil.resolveModel(model)); return model; From caebe767b02d4f34dfb7ecadf3009c4b2845a5a7 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:28:30 +0200 Subject: [PATCH 10/12] fix test --- declarative-config-bridge/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts index 66fb4658b..1da7432d0 100644 --- a/declarative-config-bridge/build.gradle.kts +++ b/declarative-config-bridge/build.gradle.kts @@ -14,4 +14,5 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") + testImplementation("org.mockito:mockito-inline") } From 2ad94463c1a2e931d9961c7ebeaef25225f24522 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:51:19 +0200 Subject: [PATCH 11/12] add property translation for inferred spans --- .../autoconfigure/ConfigPropertiesUtil.java | 10 +++- .../DeclarativeConfigPropertiesBridge.java | 16 ++++++- ...DeclarativeConfigPropertiesBridgeTest.java | 48 ++++++++++++------- inferred-spans/build.gradle.kts | 4 ++ .../InferredSpansCustomizerProvider.java | 29 +++++++++++ 5 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java index 63db139f9..e5167c966 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -12,6 +12,8 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.util.Collections; +import java.util.Map; public class ConfigPropertiesUtil { private ConfigPropertiesUtil() {} @@ -33,7 +35,7 @@ public static ConfigProperties resolveConfigProperties( instrumentationConfig = DeclarativeConfigProperties.empty(); } - return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + return new DeclarativeConfigPropertiesBridge(instrumentationConfig, Collections.emptyMap()); } // Should never happen throw new IllegalStateException( @@ -41,13 +43,17 @@ public static ConfigProperties resolveConfigProperties( } public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model) { + return resolveModel(model, Collections.emptyMap()); + } + + public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model, Map translationMap) { SdkConfigProvider configProvider = SdkConfigProvider.create(model); DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); if (instrumentationConfig == null) { instrumentationConfig = DeclarativeConfigProperties.empty(); } - return new DeclarativeConfigPropertiesBridge(instrumentationConfig); + return new DeclarativeConfigPropertiesBridge(instrumentationConfig, translationMap); } public static String propertyYamlPath(String propertyName) { diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java index 47173f7ac..0cb6d17d6 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -52,9 +52,12 @@ final class DeclarativeConfigPropertiesBridge implements ConfigProperties { // The node at .instrumentation.java private final DeclarativeConfigProperties instrumentationJavaNode; + private final Map translationMap; - DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) { + DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode, + Map translationMap) { instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); + this.translationMap = translationMap; } @Nullable @@ -134,7 +137,7 @@ private T getPropertyValue( return null; } - String[] segments = getSegments(property); + String[] segments = getSegments(translate(property)); if (segments.length == 0) { return null; } @@ -151,6 +154,15 @@ private T getPropertyValue( return extractor.apply(target, lastPart); } + private String translate(String property) { + for (Map.Entry entry : translationMap.entrySet()) { + if (property.startsWith(entry.getKey())) { + return entry.getValue() + property.substring(entry.getKey().length()); + } + } + return property; + } + private static String[] getSegments(String property) { if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java index caadda60b..9ed47cd8c 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -4,9 +4,9 @@ */ package io.opentelemetry.contrib.sdk.autoconfigure; /* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ import static org.assertj.core.api.Assertions.assertThat; @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -57,13 +58,7 @@ class DeclarativeConfigPropertiesBridgeTest { @BeforeEach void setup() { - OpenTelemetryConfigurationModel model = - DeclarativeConfiguration.parse( - new ByteArrayInputStream(YAML.getBytes(StandardCharsets.UTF_8))); - SdkConfigProvider configProvider = SdkConfigProvider.create(model); - bridge = - new DeclarativeConfigPropertiesBridge( - Objects.requireNonNull(configProvider.getInstrumentationConfig())); + bridge = createBridge(Collections.emptyMap()); OpenTelemetryConfigurationModel emptyModel = new OpenTelemetryConfigurationModel() @@ -71,7 +66,18 @@ void setup() { SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel); emptyBridge = new DeclarativeConfigPropertiesBridge( - Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig())); + Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig()), + Collections.emptyMap()); + } + + private static DeclarativeConfigPropertiesBridge createBridge( + Map translationMap) { + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(YAML.getBytes(StandardCharsets.UTF_8))); + return new DeclarativeConfigPropertiesBridge( + Objects.requireNonNull(SdkConfigProvider.create(model).getInstrumentationConfig()), + translationMap); } @Test @@ -126,21 +132,31 @@ void getProperties() { assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L)) .isEqualTo(1L); assertThat( - bridge.getDuration( - "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) + bridge.getDuration( + "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) .isEqualTo(Duration.ofMillis(1)); assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1)) .isEqualTo(1.1); assertThat( - bridge.getList( - "otel.instrumentation.other-instrumentation.list_key", - Arrays.asList("value1", "value2"))) + bridge.getList( + "otel.instrumentation.other-instrumentation.list_key", + Arrays.asList("value1", "value2"))) .isEqualTo(Arrays.asList("value1", "value2")); assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap)) .isEqualTo(expectedMap); + } + @Test + void vendor() { // verify vendor specific property names are preserved in unchanged form (prefix is not stripped // as for otel.instrumentation.*) assertThat(bridge.getBoolean("acme.full_name.preserved")).isTrue(); } + + @Test + void translation() { + DeclarativeConfigPropertiesBridge propertiesBridge = createBridge( + Collections.singletonMap("acme", "acme.full_name")); + assertThat(propertiesBridge.getBoolean("acme.preserved")).isTrue(); + } } diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index 98d5e33a3..bc89d6da9 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -9,10 +9,13 @@ description = "OpenTelemetry Java profiling based inferred spans module" otelJava.moduleName.set("io.opentelemetry.contrib.inferredspans") dependencies { + implementation(project(":declarative-config-bridge")) + annotationProcessor("com.google.auto.service:auto-service") compileOnly("com.google.auto.service:auto-service-annotations") compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") implementation("com.lmax:disruptor") implementation("org.jctools:jctools-core") @@ -25,6 +28,7 @@ dependencies { testImplementation("io.opentelemetry.semconv:opentelemetry-semconv") testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-api-incubator") testImplementation("io.opentelemetry:opentelemetry-exporter-logging") diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java new file mode 100644 index 000000000..fa55f06e4 --- /dev/null +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.inferredspans; + +import com.google.auto.service.AutoService; +import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import java.util.Collections; + +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class InferredSpansCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model, + Collections.singletonMap("otel.inferred.spans", "inferred_spans")); + + return model; + }); + } + +} From 8596bb4162d20c25c43f34a0869d7e6ba0f402fc Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:53:55 +0000 Subject: [PATCH 12/12] ./gradlew spotlessApply --- .../autoconfigure/ConfigPropertiesUtil.java | 3 ++- .../DeclarativeConfigPropertiesBridge.java | 4 ++-- ...DeclarativeConfigPropertiesBridgeTest.java | 20 +++++++++---------- .../InferredSpansCustomizerProvider.java | 6 +++--- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java index e5167c966..e83b72e0d 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/ConfigPropertiesUtil.java @@ -46,7 +46,8 @@ public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel mode return resolveModel(model, Collections.emptyMap()); } - public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model, Map translationMap) { + public static ConfigProperties resolveModel( + OpenTelemetryConfigurationModel model, Map translationMap) { SdkConfigProvider configProvider = SdkConfigProvider.create(model); DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig(); if (instrumentationConfig == null) { diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java index 0cb6d17d6..11a16afbb 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -54,8 +54,8 @@ final class DeclarativeConfigPropertiesBridge implements ConfigProperties { private final DeclarativeConfigProperties instrumentationJavaNode; private final Map translationMap; - DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode, - Map translationMap) { + DeclarativeConfigPropertiesBridge( + DeclarativeConfigProperties instrumentationNode, Map translationMap) { instrumentationJavaNode = instrumentationNode.getStructured("java", empty()); this.translationMap = translationMap; } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java index 9ed47cd8c..a7722c5b9 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -4,9 +4,9 @@ */ package io.opentelemetry.contrib.sdk.autoconfigure; /* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ import static org.assertj.core.api.Assertions.assertThat; @@ -132,15 +132,15 @@ void getProperties() { assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L)) .isEqualTo(1L); assertThat( - bridge.getDuration( - "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) + bridge.getDuration( + "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) .isEqualTo(Duration.ofMillis(1)); assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1)) .isEqualTo(1.1); assertThat( - bridge.getList( - "otel.instrumentation.other-instrumentation.list_key", - Arrays.asList("value1", "value2"))) + bridge.getList( + "otel.instrumentation.other-instrumentation.list_key", + Arrays.asList("value1", "value2"))) .isEqualTo(Arrays.asList("value1", "value2")); assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap)) .isEqualTo(expectedMap); @@ -155,8 +155,8 @@ void vendor() { @Test void translation() { - DeclarativeConfigPropertiesBridge propertiesBridge = createBridge( - Collections.singletonMap("acme", "acme.full_name")); + DeclarativeConfigPropertiesBridge propertiesBridge = + createBridge(Collections.singletonMap("acme", "acme.full_name")); assertThat(propertiesBridge.getBoolean("acme.preserved")).isTrue(); } } diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java index fa55f06e4..47a3c61a8 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java @@ -19,11 +19,11 @@ public class InferredSpansCustomizerProvider implements DeclarativeConfiguration public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { - ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model, - Collections.singletonMap("otel.inferred.spans", "inferred_spans")); + ConfigProperties configProperties = + ConfigPropertiesUtil.resolveModel( + model, Collections.singletonMap("otel.inferred.spans", "inferred_spans")); return model; }); } - }