Repository navigation
Add experimental SdkResourceProvider implementation #8865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e4d4d2f
b82c1bd
79fd5a1
537723e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.internal; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
|
||
| import io.opentelemetry.api.common.AttributeKey; | ||
| import io.opentelemetry.api.common.Attributes; | ||
| import io.opentelemetry.sdk.OpenTelemetrySdk; | ||
| import io.opentelemetry.sdk.logs.SdkLoggerProvider; | ||
| import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; | ||
| import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil; | ||
| import io.opentelemetry.sdk.metrics.SdkMeterProvider; | ||
| import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; | ||
| import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil; | ||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import io.opentelemetry.sdk.resources.internal.SdkResourceProvider; | ||
| import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; | ||
| import io.opentelemetry.sdk.trace.SdkTracerProvider; | ||
| import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; | ||
| import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; | ||
| import javax.annotation.Nullable; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class ExtendedOpenTelemetrySdkTest { | ||
|
|
||
| private static final Resource RESOURCE_A = | ||
| Resource.create(Attributes.of(AttributeKey.stringKey("a"), "1")); | ||
| private static final Resource RESOURCE_B = | ||
| Resource.create(Attributes.of(AttributeKey.stringKey("b"), "2")); | ||
|
|
||
| @Test | ||
| void getSdkResourceProvider_defaultResources_returnsAutoWrapped() { | ||
| ExtendedOpenTelemetrySdk sdk = buildExtended(null, null, null, null, null, null); | ||
|
|
||
| assertThat(sdk.getSdkResourceProvider().getResource()).isEqualTo(Resource.getDefault()); | ||
| } | ||
|
|
||
| @Test | ||
| void getSdkResourceProvider_sameSdkResourceProviderOnAll_returnsIt() { | ||
| SdkResourceProvider resourceProvider = SdkResourceProvider.create(RESOURCE_A); | ||
| ExtendedOpenTelemetrySdk sdk = | ||
| buildExtended(null, resourceProvider, null, resourceProvider, null, resourceProvider); | ||
|
|
||
| assertThat(sdk.getSdkResourceProvider()).isSameAs(resourceProvider); | ||
| } | ||
|
|
||
| @Test | ||
| void getSdkResourceProvider_equivalentAutoWrappedResources_returnsFirst() { | ||
| // Distinct RP instances resolving to the same Resource: composite check is equal-by-Resource | ||
| // so this must succeed. | ||
| ExtendedOpenTelemetrySdk sdk = | ||
| buildExtended(RESOURCE_A, null, RESOURCE_A, null, RESOURCE_A, null); | ||
|
|
||
| assertThat(sdk.getSdkResourceProvider().getResource()).isEqualTo(RESOURCE_A); | ||
| } | ||
|
|
||
| @Test | ||
| void getSdkResourceProvider_mismatchedResources_throws() { | ||
| ExtendedOpenTelemetrySdk sdk = | ||
| buildExtended(RESOURCE_A, null, RESOURCE_B, null, RESOURCE_A, null); | ||
|
|
||
| assertThatThrownBy(sdk::getSdkResourceProvider) | ||
| .isInstanceOf(IllegalStateException.class) | ||
| .hasMessageContaining("configured with different"); | ||
| } | ||
|
|
||
| private static ExtendedOpenTelemetrySdk buildExtended( | ||
| @Nullable Resource tracerResource, | ||
| @Nullable SdkResourceProvider tracerRp, | ||
| @Nullable Resource meterResource, | ||
| @Nullable SdkResourceProvider meterRp, | ||
| @Nullable Resource loggerResource, | ||
| @Nullable SdkResourceProvider loggerRp) { | ||
| SdkTracerProviderBuilder tracerBuilder = SdkTracerProvider.builder(); | ||
| if (tracerResource != null) { | ||
| tracerBuilder.setResource(tracerResource); | ||
| } | ||
| if (tracerRp != null) { | ||
| SdkTracerProviderUtil.setSdkResourceProvider(tracerBuilder, tracerRp); | ||
| } | ||
| SdkMeterProviderBuilder meterBuilder = | ||
| SdkMeterProvider.builder().registerMetricReader(InMemoryMetricReader.create()); | ||
| if (meterResource != null) { | ||
| meterBuilder.setResource(meterResource); | ||
| } | ||
| if (meterRp != null) { | ||
| SdkMeterProviderUtil.setSdkResourceProvider(meterBuilder, meterRp); | ||
| } | ||
| SdkLoggerProviderBuilder loggerBuilder = SdkLoggerProvider.builder(); | ||
| if (loggerResource != null) { | ||
| loggerBuilder.setResource(loggerResource); | ||
| } | ||
| if (loggerRp != null) { | ||
| SdkLoggerProviderUtil.setSdkResourceProvider(loggerBuilder, loggerRp); | ||
| } | ||
| return (ExtendedOpenTelemetrySdk) | ||
| OpenTelemetrySdk.builder() | ||
| .setTracerProvider(tracerBuilder.build()) | ||
| .setMeterProvider(meterBuilder.build()) | ||
| .setLoggerProvider(loggerBuilder.build()) | ||
| .build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.resources.internal; | ||
|
|
||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import javax.annotation.Nullable; | ||
|
|
||
| final class ConstantResourceDetector implements ResourceDetector { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oooh - interesting. Do you think this needs to be one of the 'out of the box' specified detectors?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure. I did this because I had to. I think it makes sense to build SdkResourceProvider around the idea that resource attributes may change at runtime via entity descriptive attributes, but in practice all resources today (and most in the future) are statically evaluated at config time. So having syntactic sugar to reflect the common case is a must. BTW I decided (b82c1bd) that the whole notion of a ResourceDetector should be hidden from users. Given that we already have Instead, we can just have methods in |
||
|
|
||
| @Nullable private final String name; | ||
| private final Resource resource; | ||
|
|
||
| ConstantResourceDetector(@Nullable String name, Resource resource) { | ||
| this.name = name; | ||
| this.resource = resource; | ||
| } | ||
|
|
||
| @Nullable | ||
| @Override | ||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| @Override | ||
| public Resource getResource() { | ||
| return resource; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "ConstantResourceDetector{name=" + name + ", resource=" + resource + "}"; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.resources.internal; | ||
|
|
||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import javax.annotation.Nullable; | ||
|
|
||
| /** A source of a {@link Resource} to be merged by an {@link SdkResourceProvider}. */ | ||
| interface ResourceDetector { | ||
|
|
||
| // TODO(jack-berg): Add shouldReinvoke() or equivalent to distinguish static detectors from | ||
| // detectors whose entity | ||
| // descriptive attributes can change at runtime. | ||
|
|
||
| /** | ||
| * Returns a diagnostic label for logging / debugging, or {@code null} if unnamed. This is not a | ||
| * detector lookup identifier and need not be unique. | ||
| */ | ||
| @Nullable | ||
| String getName(); | ||
|
|
||
| /** Returns the {@link Resource} detected by this detector. */ | ||
| Resource getResource(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.resources.internal; | ||
|
|
||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Provides the {@link Resource} used by the SDK by merging the results of a configured, ordered | ||
| * list of {@link ResourceDetector}s. | ||
| * | ||
| * <p>All detectors are invoked once when the provider is built. The merged {@link Resource} is | ||
| * cached for the lifetime of this provider. | ||
| * | ||
| * <p>This class is internal and experimental. Its APIs are unstable and can change at any time. Its | ||
| * APIs (or a version of them) may be promoted to the public stable API in the future, but no | ||
| * guarantees are made. | ||
| */ | ||
| public final class SdkResourceProvider { | ||
|
|
||
| private final List<ResourceDetector> detectors; | ||
| // TODO: Add TTL-based cache refresh that re-invokes only refreshable detectors while keeping | ||
| // hot-path resource reads cheap. | ||
| private final Resource cachedResource; | ||
|
|
||
| private SdkResourceProvider(List<ResourceDetector> detectors, Resource cachedResource) { | ||
| this.detectors = detectors; | ||
| this.cachedResource = cachedResource; | ||
| } | ||
|
|
||
| static SdkResourceProvider createFromDetectors(List<ResourceDetector> detectors) { | ||
| List<ResourceDetector> detectorSnapshot = | ||
| Collections.unmodifiableList(new ArrayList<>(detectors)); | ||
| return new SdkResourceProvider(detectorSnapshot, merge(detectorSnapshot)); | ||
| } | ||
|
|
||
| /** Returns a new {@link SdkResourceProviderBuilder}. */ | ||
| public static SdkResourceProviderBuilder builder() { | ||
| return new SdkResourceProviderBuilder(); | ||
| } | ||
|
|
||
| /** Returns an {@link SdkResourceProvider} backed by a single constant {@link Resource}. */ | ||
| public static SdkResourceProvider create(Resource resource) { | ||
| requireNonNull(resource, "resource"); | ||
| return builder().addConstantResource(resource).build(); | ||
| } | ||
|
|
||
| /** Returns the merged {@link Resource}. */ | ||
| public Resource getResource() { | ||
| return cachedResource; | ||
| } | ||
|
|
||
| private static Resource merge(List<ResourceDetector> detectors) { | ||
| Resource resource = Resource.empty(); | ||
| for (ResourceDetector detector : detectors) { | ||
| resource = resource.merge(detector.getResource()); | ||
|
psx95 marked this conversation as resolved.
Resolved
|
||
| } | ||
| return resource; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "SdkResourceProvider{detectors=" + detectors + "}"; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.