Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class NoSharedInternalCodeTest {
"opentelemetry-exporter-prometheus",
"opentelemetry-extension-trace-propagators",
"opentelemetry-opencensus-shim",
"opentelemetry-sdk",
"opentelemetry-sdk-logs",
"opentelemetry-sdk-metrics",
"opentelemetry-sdk-profiles",
Expand Down
17 changes: 17 additions & 0 deletions sdk/all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,20 @@ dependencies {
jmh(project(":sdk:testing"))
jmh(project(":api:incubator"))
}

testing {
suites {
register<JvmTestSuite>("testIncubating") {
dependencies {
implementation(project(":api:incubator"))
implementation(project(":sdk:testing"))
}
}
}
}

tasks {
check {
dependsOn(testing.suites)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.internal.SdkResourceProvider;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
Expand All @@ -23,21 +32,26 @@ public final class ExtendedOpenTelemetrySdk extends OpenTelemetrySdk

private final OpenTelemetrySdk openTelemetrySdk;
private final ObfuscatedConfigProvider configProvider;
@Nullable private final SdkResourceProvider resourceProvider;

private ExtendedOpenTelemetrySdk(
OpenTelemetrySdk openTelemetrySdk, SdkConfigProvider configProvider) {
OpenTelemetrySdk openTelemetrySdk,
SdkConfigProvider configProvider,
@Nullable SdkResourceProvider resourceProvider) {
super(
openTelemetrySdk.getSdkTracerProvider(),
openTelemetrySdk.getSdkMeterProvider(),
openTelemetrySdk.getSdkLoggerProvider(),
openTelemetrySdk.getPropagators());
this.openTelemetrySdk = openTelemetrySdk;
this.configProvider = new ObfuscatedConfigProvider(configProvider);
this.resourceProvider = resourceProvider;
}

public static ExtendedOpenTelemetrySdk create(
OpenTelemetrySdk openTelemetrySdk, SdkConfigProvider sdkConfigProvider) {
return new ExtendedOpenTelemetrySdk(openTelemetrySdk, sdkConfigProvider);
return new ExtendedOpenTelemetrySdk(
openTelemetrySdk, sdkConfigProvider, resolveSdkResourceProvider(openTelemetrySdk));
}

@Override
Expand All @@ -50,13 +64,48 @@ public SdkConfigProvider getSdkConfigProvider() {
return configProvider.unobfuscate();
}

/**
* Returns the {@link SdkResourceProvider} shared by the SDK's tracer, meter, and logger
* providers.
*
* @throws IllegalStateException if the three signal providers were not configured with equivalent
* resources.
*/
public SdkResourceProvider getSdkResourceProvider() {
if (resourceProvider == null) {
throw new IllegalStateException(
"SdkTracerProvider, SdkMeterProvider, and SdkLoggerProvider are configured with"
+ " different resources. Configure all three signal providers with the same"
+ " SdkResourceProvider (or the same Resource) to resolve this.");
Comment thread
breedx-splk marked this conversation as resolved.
Resolved
}
return resourceProvider;
}

@Nullable
private static SdkResourceProvider resolveSdkResourceProvider(OpenTelemetrySdk sdk) {
SdkTracerProvider tracerProvider = sdk.getSdkTracerProvider();
SdkMeterProvider meterProvider = sdk.getSdkMeterProvider();
SdkLoggerProvider loggerProvider = sdk.getSdkLoggerProvider();
SdkResourceProvider tracerRp = SdkTracerProviderUtil.getSdkResourceProvider(tracerProvider);
SdkResourceProvider meterRp = SdkMeterProviderUtil.getSdkResourceProvider(meterProvider);
SdkResourceProvider loggerRp = SdkLoggerProviderUtil.getSdkResourceProvider(loggerProvider);
Resource tracerResource = tracerRp.getResource();
if (tracerResource.equals(meterRp.getResource())
&& tracerResource.equals(loggerRp.getResource())) {
return tracerRp;
}
return null;
Comment thread
jack-berg marked this conversation as resolved.
Resolved
}

@Override
public String toString() {
return "ExtendedOpenTelemetrySdk{"
+ "openTelemetrySdk="
+ openTelemetrySdk
+ ", configProvider="
+ configProvider.unobfuscate()
+ ", resourceProvider="
+ resourceProvider
+ "}";
}

Expand Down
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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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 ResourceProvider and ComponentProvider (i.e the declarative config version), having yet another interface for providing resources is too confusing a UX.

Instead, we can just have methods in SdkResourceProviderBuilder which reflect the different styles of resources that might need to be associated with a provider:

public final class SdkResourceProviderBuilder {                                                                                                                                                                                                                                                                                                                                                                                                           
 public SdkResourceProviderBuilder addConstantResource(Resource resource);                                                                                                                                                                                                                                                                                                                                                                               
 public SdkResourceProviderBuilder addConstantResource(Resource resource, @Nullable String name);                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                         
 public SdkResourceProvider build();                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                         
 // Proposed future expansion                                                                                                                                                                                                                                                                                                                                                                                                                            
 public SdkResourceProviderBuilder addDetector(Supplier<Resource> resourceSupplier, Duration refreshInterval);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
 public SdkResourceProviderBuilder addDetector(Supplier<Resource> resourceSupplier, Duration refreshInterval, @Nullable String name);                                                                                                                                                                                                                                                                                                                                           
}   


@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());
Comment thread
psx95 marked this conversation as resolved.
Resolved
}
return resource;
}

@Override
public String toString() {
return "SdkResourceProvider{detectors=" + detectors + "}";
}
}
Loading
Loading