Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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 @@ -17,6 +17,7 @@
import io.opentelemetry.context.propagation.ContextPropagators;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -116,6 +117,19 @@ public static void set(OpenTelemetry openTelemetry) {
}
}

/**
* Sets the {@link OpenTelemetry} that should be the global instance.
*
* <p>This method calls the given {@code supplier} and calls {@link #set(OpenTelemetry)}, all
* while holding the {@link GlobalOpenTelemetry} mutex.
*/
public static void set(Supplier<OpenTelemetry> supplier) {
synchronized (mutex) {
OpenTelemetry openTelemetry = supplier.get();
set(openTelemetry);
}
}

/** Returns the globally registered {@link TracerProvider}. */
public static TracerProvider getTracerProvider() {
return get().getTracerProvider();
Expand Down
4 changes: 3 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-api.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-api-1.52.0-SNAPSHOT.jar against opentelemetry-api-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.api.GlobalOpenTelemetry (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) void set(java.util.function.Supplier<io.opentelemetry.api.OpenTelemetry>)
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -424,6 +426,25 @@ AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader compone
* the settings of this {@link AutoConfiguredOpenTelemetrySdkBuilder}.
*/
public AutoConfiguredOpenTelemetrySdk build() {
if (!setResultAsGlobal) {
return buildImpl();
}
AtomicReference<AutoConfiguredOpenTelemetrySdk> autoConfiguredRef = new AtomicReference<>();
GlobalOpenTelemetry.set(
() -> {
AutoConfiguredOpenTelemetrySdk sdk = buildImpl();
autoConfiguredRef.set(sdk);
return sdk.getOpenTelemetrySdk();
});
AutoConfiguredOpenTelemetrySdk sdk = Objects.requireNonNull(autoConfiguredRef.get());
logger.log(
Level.FINE,
"Global OpenTelemetry set to {0} by autoconfiguration",
sdk.getOpenTelemetrySdk());
return sdk;
}

private AutoConfiguredOpenTelemetrySdk buildImpl() {
SpiHelper spiHelper = SpiHelper.create(componentLoader);
if (!customized) {
customized = true;
Expand All @@ -440,8 +461,10 @@ public AutoConfiguredOpenTelemetrySdk build() {
maybeConfigureFromFile(config, componentLoader);
if (fromFileConfiguration != null) {
maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk());
maybeSetAsGlobal(
fromFileConfiguration.getOpenTelemetrySdk(), fromFileConfiguration.getConfigProvider());
Object configProvider = fromFileConfiguration.getConfigProvider();
if (setResultAsGlobal && INCUBATOR_AVAILABLE && configProvider != null) {
IncubatingUtil.setGlobalConfigProvider(configProvider);
}
return fromFileConfiguration;
}

Expand All @@ -467,7 +490,6 @@ public AutoConfiguredOpenTelemetrySdk build() {

OpenTelemetrySdk openTelemetrySdk = sdkBuilder.build();
maybeRegisterShutdownHook(openTelemetrySdk);
maybeSetAsGlobal(openTelemetrySdk, null);
callAutoConfigureListeners(spiHelper, openTelemetrySdk);

return AutoConfiguredOpenTelemetrySdk.create(openTelemetrySdk, resource, config, null);
Expand Down Expand Up @@ -572,19 +594,6 @@ private void maybeRegisterShutdownHook(OpenTelemetrySdk openTelemetrySdk) {
Runtime.getRuntime().addShutdownHook(shutdownHook(openTelemetrySdk));
}

private void maybeSetAsGlobal(
OpenTelemetrySdk openTelemetrySdk, @Nullable Object configProvider) {
if (!setResultAsGlobal) {
return;
}
GlobalOpenTelemetry.set(openTelemetrySdk);
if (INCUBATOR_AVAILABLE && configProvider != null) {
IncubatingUtil.setGlobalConfigProvider(configProvider);
}
logger.log(
Level.FINE, "Global OpenTelemetry set to {0} by autoconfiguration", openTelemetrySdk);
}

// Visible for testing
void callAutoConfigureListeners(SpiHelper spiHelper, OpenTelemetrySdk openTelemetrySdk) {
for (AutoConfigureListener listener : spiHelper.getListeners()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,19 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
Expand Down Expand Up @@ -674,4 +678,53 @@ void configurationError_ClosesResources() {

logs.assertContains("Error closing io.opentelemetry.sdk.trace.SdkTracerProvider: Error!");
}

@Test
@SuppressWarnings({"unchecked", "FutureReturnValueIgnored"})
void test() throws Exception {
// Idea of the test:
// 1. Thread 1 calls AutoConfiguredOpenTelemetrySdkBuilder#build
// 2. It acquires the mutex in GlobalOpenTelemetry.set(Supplier<OpenTelemetry>)
// 3. While holding the mutex before the call to GlobalOpenTelemetry.set(OpenTelemetry),
// it spawns Thread 2 which calls GlobalOpenTelemetry.get
// 4. We test that Thread 2 does not fail, and that its result is the same OpenTelemetry
// instance embedded in the AutoConfiguredOpenTelemetrySdk
SynchronousQueue<OpenTelemetry> gotGetResult = new SynchronousQueue<>();

try (MockedStatic<GlobalOpenTelemetry> mockGot =
Mockito.mockStatic(GlobalOpenTelemetry.class)) {
mockGot.when(GlobalOpenTelemetry::get).thenCallRealMethod();
mockGot.when(() -> GlobalOpenTelemetry.set(any(Supplier.class))).thenCallRealMethod();
mockGot
.when(() -> GlobalOpenTelemetry.set(any(OpenTelemetry.class)))
.then(
invocation -> {
CompletableFuture.supplyAsync(GlobalOpenTelemetry::get)
.handle(
(sdk, exc) -> {
if (exc != null) {
Assertions.fail(exc);
} else {
try {
gotGetResult.put(sdk);
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
Assertions.fail(exception);
}
}
return null;
});
// Give Thread 2 some time to try obtaining the mutex and getting blocked
Thread.sleep(500);
return invocation.callRealMethod();
});

AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
builder.setResultAsGlobal().build();

assertThat(gotGetResult.poll(3, TimeUnit.SECONDS))
.extracting("delegate")
.isSameAs(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk());
}
}
}
Loading