diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 70e3507d22..6db27ec1fc 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -74,5 +74,12 @@
'com.diffplug.spotless{/,}**',
],
},
+ {
+ // equals verifier v4+ requires java 17+
+ groupName: 'nl.jqno.equalsverifier',
+ matchPackageNames: [ 'equalsverifier'],
+ matchUpdateTypes: [ 'major' ],
+ enabled: false
+ }
],
}
diff --git a/.github/workflows/benchmark-tags.yml b/.github/workflows/benchmark-tags.yml
index 95b4675f5d..b6485ae808 100644
--- a/.github/workflows/benchmark-tags.yml
+++ b/.github/workflows/benchmark-tags.yml
@@ -56,7 +56,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Run jmh
run: ./gradlew jmhJar
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 3ddb3c6ff5..310be8bf96 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -26,7 +26,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Run jmh
run: ./gradlew jmhJar
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 047e602ac8..5e8ccb4e82 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -69,7 +69,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Build
run: >
./gradlew build
@@ -145,7 +145,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
# skipping release branches because the versions in those branches are not snapshots
# (also this skips pull requests)
if: ${{ github.ref_name == 'main' && github.repository == 'open-telemetry/opentelemetry-java' }}
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index eadfaa7abc..740ae160f7 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -12,7 +12,7 @@ on:
- release/*
- benchmarks
schedule:
- - cron: "29 13 * * 2" # weekly at 13:29 UTC on Tuesday
+ - cron: "23 16 * * 2" # weekly at 16:23 UTC on Tuesday
permissions:
contents: read
@@ -23,32 +23,43 @@ jobs:
contents: read
actions: read # for github/codeql-action/init to get workflow details
security-events: write # for github/codeql-action/analyze to upload SARIF results
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: actions
+ - language: java
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Java 17
+ if: matrix.language == 'java'
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
distribution: temurin
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ if: matrix.language == 'java'
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Initialize CodeQL
- uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
+ uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
- languages: java, actions
+ languages: ${{ matrix.language }}
# using "latest" helps to keep up with the latest Kotlin support
# see https://github.com/github/codeql-action/issues/1555#issuecomment-1452228433
tools: latest
- name: Assemble
+ if: matrix.language == 'java'
# --no-build-cache is required for codeql to analyze all modules
# --no-daemon is required for codeql to observe the compilation
# (see https://docs.github.com/en/code-security/codeql-cli/getting-started-with-the-codeql-cli/preparing-your-code-for-codeql-analysis#specifying-build-commands)
run: ./gradlew assemble --no-build-cache --no-daemon
- name: Perform CodeQL analysis
- uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
+ uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
+ with:
+ category: "/language:${{matrix.language}}"
\ No newline at end of file
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
index a9e18e8aa4..ded780070b 100644
--- a/.github/workflows/gradle-wrapper-validation.yml
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -13,4 +13,4 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: gradle/actions/wrapper-validation@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ - uses: gradle/actions/wrapper-validation@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
diff --git a/.github/workflows/javadoc-crawler.yml b/.github/workflows/javadoc-crawler.yml
index cd87b005f7..9567c38506 100644
--- a/.github/workflows/javadoc-crawler.yml
+++ b/.github/workflows/javadoc-crawler.yml
@@ -20,7 +20,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Run crawler
run: ./gradlew :javadoc-crawler:crawl
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
index 352cf133f0..cff5b894fc 100644
--- a/.github/workflows/ossf-scorecard.yml
+++ b/.github/workflows/ossf-scorecard.yml
@@ -42,6 +42,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
+ uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
sarif_file: results.sarif
diff --git a/.github/workflows/owasp-dependency-check-daily.yml b/.github/workflows/owasp-dependency-check-daily.yml
index 8d795c6e7f..dc485ade1b 100644
--- a/.github/workflows/owasp-dependency-check-daily.yml
+++ b/.github/workflows/owasp-dependency-check-daily.yml
@@ -22,7 +22,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Check dependencies
run: ./gradlew dependencyCheckAnalyze
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ada71742b2..abc1423885 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,7 +28,7 @@ jobs:
java-version: 17
- name: Set up gradle
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
+ uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
- name: Build and publish artifacts
run: ./gradlew assemble publishToSonatype closeAndReleaseSonatypeStagingRepository
diff --git a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
index 230a85e06a..83e70bf7d8 100644
--- a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
+++ b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
@@ -17,6 +17,7 @@ import io.opentelemetry.api.trace.TracerProvider;
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;
@@ -116,6 +117,19 @@ public final class GlobalOpenTelemetry {
}
}
+ /**
+ * Sets the {@link OpenTelemetry} that should be the global instance.
+ *
+ *
This method calls the given {@code supplier} and calls {@link #set(OpenTelemetry)}, all
+ * while holding the {@link GlobalOpenTelemetry} mutex.
+ */
+ public static void set(Supplier supplier) {
+ synchronized (mutex) {
+ OpenTelemetry openTelemetry = supplier.get();
+ set(openTelemetry);
+ }
+ }
+
/** Returns the globally registered {@link TracerProvider}. */
public static TracerProvider getTracerProvider() {
return get().getTracerProvider();
diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
index 6dc35c271c..54b6269d81 100644
--- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
@@ -42,7 +42,7 @@ java {
checkstyle {
configDirectory.set(file("$rootDir/buildscripts/"))
- toolVersion = "10.25.0"
+ toolVersion = "10.26.0"
isIgnoreFailures = false
configProperties["rootDir"] = rootDir
}
diff --git a/context/src/main/java/io/opentelemetry/context/LazyStorage.java b/context/src/main/java/io/opentelemetry/context/LazyStorage.java
index 1727ee06fe..5cce59d253 100644
--- a/context/src/main/java/io/opentelemetry/context/LazyStorage.java
+++ b/context/src/main/java/io/opentelemetry/context/LazyStorage.java
@@ -104,7 +104,8 @@ final class LazyStorage {
}
List providers = new ArrayList<>();
- for (ContextStorageProvider provider : ServiceLoader.load(ContextStorageProvider.class)) {
+ for (ContextStorageProvider provider :
+ ServiceLoader.load(ContextStorageProvider.class, LazyStorage.class.getClassLoader())) {
if (provider
.getClass()
.getName()
diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts
index aabfe3de3f..b01c72f28a 100644
--- a/dependencyManagement/build.gradle.kts
+++ b/dependencyManagement/build.gradle.kts
@@ -13,7 +13,7 @@ val DEPENDENCY_BOMS = listOf(
// (which is EPL licensed) or armeria bom (which is Apache licensed but is getting flagged
// by FOSSA for containing EPL-licensed)
- "com.fasterxml.jackson:jackson-bom:2.19.0",
+ "com.fasterxml.jackson:jackson-bom:2.19.1",
"com.google.guava:guava-bom:33.4.8-jre",
"com.google.protobuf:protobuf-bom:4.31.1",
"com.squareup.okhttp3:okhttp-bom:4.12.0",
@@ -23,7 +23,7 @@ val DEPENDENCY_BOMS = listOf(
"io.zipkin.brave:brave-bom:6.3.0",
"io.zipkin.reporter2:zipkin-reporter-bom:3.5.1",
"org.assertj:assertj-bom:3.27.3",
- "org.testcontainers:testcontainers-bom:1.21.1",
+ "org.testcontainers:testcontainers-bom:1.21.2",
"org.snakeyaml:snakeyaml-engine:2.9"
)
@@ -68,7 +68,7 @@ val DEPENDENCIES = listOf(
"io.prometheus:prometheus-metrics-exposition-formats-no-protobuf:${prometheusServerVersion}",
"javax.annotation:javax.annotation-api:1.3.2",
"com.github.stefanbirkner:system-rules:1.19.0",
- "com.google.api.grpc:proto-google-common-protos:2.58.0",
+ "com.google.api.grpc:proto-google-common-protos:2.58.2",
"com.google.code.findbugs:jsr305:3.0.2",
"com.google.guava:guava-beta-checker:1.0",
"com.sun.net.httpserver:http:20070405",
diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt
index ba433bd6ed..3a33f42b55 100644
--- a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt
+++ b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt
@@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-api-1.52.0-SNAPSHOT.jar against opentelemetry-api-1.51.0.jar
-No changes.
\ No newline at end of file
+*** 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)
diff --git a/integration-tests/tracecontext/docker/Dockerfile b/integration-tests/tracecontext/docker/Dockerfile
index 39d52f3b97..02f577a93c 100644
--- a/integration-tests/tracecontext/docker/Dockerfile
+++ b/integration-tests/tracecontext/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.13.4@sha256:eb120d016adcbc8bac194e15826bbb4f1d1569d298d8817bb5049ed5e59f41d9 AS build
+FROM python:3.13.5@sha256:5f69d22a88dd4cc4ee1576def19aef48c8faa1b566054c44291183831cbad13b AS build
# Main branch SHA as of April-1-2021
ARG TRACECONTEXT_GIT_TAG="dcd3ad9b7d6ac36f70ff3739874b73c11b0302a1"
@@ -11,7 +11,7 @@ RUN unzip trace-context.zip
RUN rm trace-context.zip
RUN mv trace-context-${TRACECONTEXT_GIT_TAG}/test /tracecontext-testsuite
-FROM python:3.13.4-slim@sha256:d97b595c5f4ac718102e5a5a91adaf04b22e852961a698411637c718d45867c8
+FROM python:3.13.5-slim@sha256:f2fdaec50160418e0c2867ba3e254755edd067171725886d5d303fd7057bbf81
RUN pip install aiohttp
diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java
index e12848f0d8..124231fa32 100644
--- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java
+++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java
@@ -41,6 +41,8 @@ import java.util.Collections;
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;
@@ -424,6 +426,25 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
* the settings of this {@link AutoConfiguredOpenTelemetrySdkBuilder}.
*/
public AutoConfiguredOpenTelemetrySdk build() {
+ if (!setResultAsGlobal) {
+ return buildImpl();
+ }
+ AtomicReference 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;
@@ -440,8 +461,10 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
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;
}
@@ -467,7 +490,6 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
OpenTelemetrySdk openTelemetrySdk = sdkBuilder.build();
maybeRegisterShutdownHook(openTelemetrySdk);
- maybeSetAsGlobal(openTelemetrySdk, null);
callAutoConfigureListeners(spiHelper, openTelemetrySdk);
return AutoConfiguredOpenTelemetrySdk.create(openTelemetrySdk, resource, config, null);
@@ -572,19 +594,6 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
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()) {
diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
index c7ae65de9a..45d2630832 100644
--- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
+++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
@@ -58,6 +58,7 @@ import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
@@ -65,7 +66,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -674,4 +681,66 @@ class AutoConfiguredOpenTelemetrySdkTest {
logs.assertContains("Error closing io.opentelemetry.sdk.trace.SdkTracerProvider: Error!");
}
+
+ @Test
+ void globalOpenTelemetryLock() throws InterruptedException, ExecutionException, TimeoutException {
+ CountDownLatch autoconfigStarted = new CountDownLatch(1);
+ CountDownLatch completeAutoconfig = new CountDownLatch(1);
+ ExecutorService executorService = Executors.newFixedThreadPool(2);
+
+ // Submit a future to autoconfigure the SDK and set the result as global. Add a customization
+ // hook which blocks until we say so.
+ CompletableFuture autoConfiguredOpenTelemetryFuture =
+ CompletableFuture.supplyAsync(
+ () ->
+ builder
+ .addLoggerProviderCustomizer(
+ (sdkLoggerProviderBuilder, configProperties) -> {
+ autoconfigStarted.countDown();
+ try {
+ completeAutoconfig.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return sdkLoggerProviderBuilder;
+ })
+ .setResultAsGlobal()
+ .build()
+ .getOpenTelemetrySdk(),
+ executorService);
+
+ // Wait for autoconfiguration to enter our callback, then try to get an instance of
+ // GlobalOpenTelemetry. GlobalOpenTelemetry.get() should block until we release the
+ // completeAutoconfig latch and allow autoconfiguration to complete.
+ autoconfigStarted.await();
+ CompletableFuture globalOpenTelemetryFuture =
+ CompletableFuture.supplyAsync(GlobalOpenTelemetry::get, executorService);
+ Thread.sleep(10);
+ assertThat(globalOpenTelemetryFuture.isDone()).isFalse();
+ assertThat(autoConfiguredOpenTelemetryFuture.isDone()).isFalse();
+
+ // Release the latch, allowing autoconfiguration to complete. Confirm that our
+ // GlobalOpenTelemetry.get() future resolved to the same instance as autoconfiguration.
+ completeAutoconfig.countDown();
+ assertThat(unobfuscate(globalOpenTelemetryFuture.get(10, TimeUnit.SECONDS)))
+ .isSameAs(autoConfiguredOpenTelemetryFuture.get(10, TimeUnit.SECONDS));
+
+ // Cleanup
+ executorService.shutdown();
+ autoConfiguredOpenTelemetryFuture.get().shutdown().join(10, TimeUnit.SECONDS);
+ GlobalOpenTelemetry.resetForTest();
+ }
+
+ private static OpenTelemetry unobfuscate(OpenTelemetry openTelemetry) {
+ try {
+ Field delegateField =
+ Class.forName("io.opentelemetry.api.GlobalOpenTelemetry$ObfuscatedOpenTelemetry")
+ .getDeclaredField("delegate");
+ delegateField.setAccessible(true);
+ Object delegate = delegateField.get(openTelemetry);
+ return (OpenTelemetry) delegate;
+ } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
+ throw new IllegalStateException("Error unobfuscating OpenTelemetry", e);
+ }
+ }
}
diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
index 85a6ab085f..c5e6c15066 100644
--- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
+++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
@@ -23,7 +23,7 @@ class DeclarativeConfigurationTest {
@Test
void configFile(@TempDir Path tempDir) throws IOException {
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " - name: service.name\n"
diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
index 345614ffc4..f92c1027cc 100644
--- a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
+++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java
@@ -62,7 +62,7 @@ class DeclarativeConfigurationTest {
@BeforeEach
void setup() throws IOException {
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " - name: service.name\n"
@@ -199,7 +199,7 @@ class DeclarativeConfigurationTest {
@Test
void configFile_Error(@TempDir Path tempDir) throws IOException {
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " - name: service.name\n"
diff --git a/sdk-extensions/incubator/build.gradle.kts b/sdk-extensions/incubator/build.gradle.kts
index 9ed2927a24..1bb858d699 100644
--- a/sdk-extensions/incubator/build.gradle.kts
+++ b/sdk-extensions/incubator/build.gradle.kts
@@ -57,7 +57,7 @@ dependencies {
// 7. deleteJs2pTmp - delete tmp directory
// ... proceed with normal sourcesJar, compileJava, etc
-val configurationTag = "0.4.0"
+val configurationTag = "1.0.0-rc.1"
val configurationRef = "refs/tags/v$configurationTag" // Replace with commit SHA to point to experiment with a specific commit
val configurationRepoZip = "https://github.com/open-telemetry/opentelemetry-configuration/archive/$configurationRef.zip"
val buildDirectory = layout.buildDirectory.asFile.get()
diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java
index a64e0b07de..313e5d50b7 100644
--- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java
+++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java
@@ -11,11 +11,12 @@ import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Objects;
+import java.util.regex.Pattern;
final class OpenTelemetryConfigurationFactory
implements Factory {
- private static final String CURRENT_SUPPORTED_FILE_FORMAT = "0.4";
+ private static final Pattern SUPPORTED_FILE_FORMATS = Pattern.compile("^(0.4)|(1.0(-rc.\\d*)?)$");
private static final OpenTelemetryConfigurationFactory INSTANCE =
new OpenTelemetryConfigurationFactory();
@@ -30,10 +31,13 @@ final class OpenTelemetryConfigurationFactory
public OpenTelemetrySdk create(
OpenTelemetryConfigurationModel model, DeclarativeConfigContext context) {
OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder();
- if (!CURRENT_SUPPORTED_FILE_FORMAT.equals(model.getFileFormat())) {
+ String fileFormat = model.getFileFormat();
+ if (fileFormat == null || !SUPPORTED_FILE_FORMATS.matcher(fileFormat).matches()) {
throw new DeclarativeConfigException(
- "Unsupported file format. Supported formats include: " + CURRENT_SUPPORTED_FILE_FORMAT);
+ "Unsupported file format. Supported formats include 0.4, 1.0*");
}
+ // TODO(jack-berg): log warning if version is not exact match, which may result in unexpected
+ // behavior for experimental properties.
if (Objects.equals(Boolean.TRUE, model.getDisabled())) {
return builder.build();
diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java
new file mode 100644
index 0000000000..a8077032ec
--- /dev/null
+++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.extension.incubator.fileconfig;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
+import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
+import io.opentelemetry.sdk.resources.Resource;
+import io.opentelemetry.sdk.resources.ResourceBuilder;
+import java.util.Collections;
+import java.util.UUID;
+
+public class ServiceResourceDetector implements ComponentProvider {
+
+ private static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name");
+ private static final AttributeKey SERVICE_INSTANCE_ID =
+ AttributeKey.stringKey("service.instance.id");
+
+ // multiple calls to this resource provider should return the same value
+ private static final String RANDOM_SERVICE_INSTANCE_ID = UUID.randomUUID().toString();
+
+ @Override
+ public Class getType() {
+ return Resource.class;
+ }
+
+ @Override
+ public String getName() {
+ return "service";
+ }
+
+ @Override
+ public Resource create(DeclarativeConfigProperties config) {
+ ResourceBuilder builder = Resource.builder();
+
+ ConfigProperties properties = DefaultConfigProperties.create(Collections.emptyMap());
+ String serviceName = properties.getString("otel.service.name");
+ if (serviceName != null) {
+ builder.put(SERVICE_NAME, serviceName).build();
+ }
+
+ builder.put(SERVICE_INSTANCE_ID, RANDOM_SERVICE_INSTANCE_ID);
+
+ return builder.build();
+ }
+}
diff --git a/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider b/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider
new file mode 100644
index 0000000000..a1a361a5f3
--- /dev/null
+++ b/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider
@@ -0,0 +1 @@
+io.opentelemetry.sdk.extension.incubator.fileconfig.ServiceResourceDetector
diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java
index 2429ae150a..62ee078286 100644
--- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java
+++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java
@@ -106,7 +106,7 @@ class DeclarativeConfigurationCreateTest {
// exporter with OTLP exporter, following by invalid batch exporter which references invalid
// exporter "foo".
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "logger_provider:\n"
+ " processors:\n"
+ " - batch:\n"
@@ -133,7 +133,7 @@ class DeclarativeConfigurationCreateTest {
@Test
void parseAndCreate_EmptyComponentProviderConfig() {
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "logger_provider:\n"
+ " processors:\n"
+ " - test:\n"
@@ -151,7 +151,7 @@ class DeclarativeConfigurationCreateTest {
@Test
void create_ModelCustomizer() {
OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel();
- model.withFileFormat("0.4");
+ model.withFileFormat("1.0-rc.1");
model.withTracerProvider(
new TracerProviderModel()
.withProcessors(
diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java
index 32a8ad878b..0147cf8b68 100644
--- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java
+++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java
@@ -113,7 +113,7 @@ class DeclarativeConfigurationParseTest {
void parse_KitchenSinkExampleFile() throws IOException {
OpenTelemetryConfigurationModel expected = new OpenTelemetryConfigurationModel();
- expected.withFileFormat("0.4");
+ expected.withFileFormat("1.0-rc.1");
expected.withDisabled(false);
expected.withLogLevel("info");
@@ -171,9 +171,9 @@ class DeclarativeConfigurationParseTest {
new ExperimentalResourceDetectorModel()
.withAdditionalProperty("host", null),
new ExperimentalResourceDetectorModel()
- .withAdditionalProperty("os", null),
+ .withAdditionalProperty("process", null),
new ExperimentalResourceDetectorModel()
- .withAdditionalProperty("process", null))))
+ .withAdditionalProperty("service", null))))
.withSchemaUrl("https://opentelemetry.io/schemas/1.16.0");
expected.withResource(resource);
@@ -705,7 +705,7 @@ class DeclarativeConfigurationParseTest {
OpenTelemetryConfigurationModel config = DeclarativeConfiguration.parse(configExampleFile);
// General config
- assertThat(config.getFileFormat()).isEqualTo("0.4");
+ assertThat(config.getFileFormat()).isEqualTo("1.0-rc.1");
assertThat(config.getResource()).isEqualTo(resource);
assertThat(config.getAttributeLimits()).isEqualTo(attributeLimits);
assertThat(config.getPropagator()).isEqualTo(propagator);
@@ -770,7 +770,7 @@ class DeclarativeConfigurationParseTest {
@Test
void parse_nullValuesParsedToEmptyObjects() {
String objectPlaceholderString =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - batch:\n"
@@ -788,7 +788,7 @@ class DeclarativeConfigurationParseTest {
new ByteArrayInputStream(objectPlaceholderString.getBytes(StandardCharsets.UTF_8)));
String noOjbectPlaceholderString =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - batch:\n"
@@ -986,7 +986,7 @@ class DeclarativeConfigurationParseTest {
@Test
void read_WithEnvironmentVariables() {
String yaml =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - batch:\n"
@@ -1005,7 +1005,7 @@ class DeclarativeConfigurationParseTest {
assertThat(model)
.isEqualTo(
new OpenTelemetryConfigurationModel()
- .withFileFormat("0.4")
+ .withFileFormat("1.0-rc.1")
.withTracerProvider(
new TracerProviderModel()
.withProcessors(
diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java
index 16ec2c284d..ba535627c0 100644
--- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java
+++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java
@@ -7,6 +7,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
@@ -63,8 +64,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
class OpenTelemetryConfigurationFactoryTest {
@@ -74,23 +79,41 @@ class OpenTelemetryConfigurationFactoryTest {
new DeclarativeConfigContext(
SpiHelper.create(OpenTelemetryConfigurationFactoryTest.class.getClassLoader()));
- @Test
- void create_InvalidFileFormat() {
- List testCases =
- Arrays.asList(
- new OpenTelemetryConfigurationModel(),
- new OpenTelemetryConfigurationModel().withFileFormat("1"));
+ @ParameterizedTest
+ @MethodSource("fileFormatArgs")
+ void create_FileFormat(String fileFormat, boolean isValid) {
+ OpenTelemetryConfigurationModel model =
+ new OpenTelemetryConfigurationModel().withFileFormat(fileFormat);
- List closeables = new ArrayList<>();
- for (OpenTelemetryConfigurationModel testCase : testCases) {
+ if (isValid) {
+ assertThatCode(() -> OpenTelemetryConfigurationFactory.getInstance().create(model, context))
+ .doesNotThrowAnyException();
+ } else {
assertThatThrownBy(
- () -> OpenTelemetryConfigurationFactory.getInstance().create(testCase, context))
+ () -> OpenTelemetryConfigurationFactory.getInstance().create(model, context))
.isInstanceOf(DeclarativeConfigException.class)
- .hasMessage("Unsupported file format. Supported formats include: 0.4");
- cleanup.addCloseables(closeables);
+ .hasMessage("Unsupported file format. Supported formats include 0.4, 1.0*");
}
}
+ private static Stream fileFormatArgs() {
+ return Stream.of(
+ // Invalid file formats
+ Arguments.of(null, false),
+ Arguments.of("0.3", false),
+ Arguments.of("a0.4", false),
+ Arguments.of("0.4a", false),
+ Arguments.of("foo", false),
+ Arguments.of("1.0-rc.a", false),
+ Arguments.of("1.0.0", false),
+ Arguments.of("1.0.3", false),
+ // Valid file formats
+ Arguments.of("0.4", true),
+ Arguments.of("1.0-rc.1", true),
+ Arguments.of("1.0-rc.2", true),
+ Arguments.of("1.0", true));
+ }
+
@Test
void create_Defaults() {
List closeables = new ArrayList<>();
@@ -99,7 +122,7 @@ class OpenTelemetryConfigurationFactoryTest {
OpenTelemetrySdk sdk =
OpenTelemetryConfigurationFactory.getInstance()
- .create(new OpenTelemetryConfigurationModel().withFileFormat("0.4"), context);
+ .create(new OpenTelemetryConfigurationModel().withFileFormat("1.0-rc.1"), context);
cleanup.addCloseable(sdk);
cleanup.addCloseables(closeables);
@@ -116,7 +139,7 @@ class OpenTelemetryConfigurationFactoryTest {
OpenTelemetryConfigurationFactory.getInstance()
.create(
new OpenTelemetryConfigurationModel()
- .withFileFormat("0.4")
+ .withFileFormat("1.0-rc.1")
.withDisabled(true)
// Logger provider configuration should be ignored since SDK is disabled
.withLoggerProvider(
@@ -210,7 +233,7 @@ class OpenTelemetryConfigurationFactoryTest {
OpenTelemetryConfigurationFactory.getInstance()
.create(
new OpenTelemetryConfigurationModel()
- .withFileFormat("0.4")
+ .withFileFormat("1.0-rc.1")
.withPropagator(
new PropagatorModel()
.withCompositeList("tracecontext,baggage,ottrace,b3multi,b3,jaeger"))
diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetectorTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetectorTest.java
new file mode 100644
index 0000000000..4778fe1b61
--- /dev/null
+++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetectorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.extension.incubator.fileconfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.sdk.resources.Resource;
+import java.util.Objects;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+
+class ServiceResourceDetectorTest {
+
+ @Test
+ void getTypeAndName() {
+ ServiceResourceDetector detector = new ServiceResourceDetector();
+
+ assertThat(detector.getType()).isEqualTo(Resource.class);
+ assertThat(detector.getName()).isEqualTo("service");
+ }
+
+ @Test
+ @ClearSystemProperty(key = "otel.service.name")
+ void create_SystemPropertySet() {
+ System.setProperty("otel.service.name", "test");
+
+ assertThat(new ServiceResourceDetector().create(DeclarativeConfigProperties.empty()))
+ .satisfies(
+ resource -> {
+ Attributes attributes = resource.getAttributes();
+ assertThat(attributes.get(AttributeKey.stringKey("service.name"))).isEqualTo("test");
+ assertThatCode(
+ () ->
+ UUID.fromString(
+ Objects.requireNonNull(
+ attributes.get(AttributeKey.stringKey("service.instance.id")))))
+ .doesNotThrowAnyException();
+ });
+ }
+
+ @Test
+ void create_NoSystemProperty() {
+ assertThat(new ServiceResourceDetector().create(DeclarativeConfigProperties.empty()))
+ .satisfies(
+ resource -> {
+ Attributes attributes = resource.getAttributes();
+ assertThat(attributes.get(AttributeKey.stringKey("service.name"))).isNull();
+ assertThatCode(
+ () ->
+ UUID.fromString(
+ Objects.requireNonNull(
+ attributes.get(AttributeKey.stringKey("service.instance.id")))))
+ .doesNotThrowAnyException();
+ });
+ }
+}
diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java
index 7f1c3cb3cc..82a2d38475 100644
--- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java
+++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
class YamlDeclarativeConfigPropertiesTest {
private static final String extendedSchema =
- "file_format: \"0.4\"\n"
+ "file_format: \"1.0-rc.1\"\n"
+ "disabled: false\n"
+ "\n"
+ "resource:\n"
@@ -69,7 +69,7 @@ class YamlDeclarativeConfigPropertiesTest {
@Test
void configurationSchema() {
// Validate can read declarative configuration schema properties
- assertThat(structuredConfigProps.getString("file_format")).isEqualTo("0.4");
+ assertThat(structuredConfigProps.getString("file_format")).isEqualTo("1.0-rc.1");
DeclarativeConfigProperties resourceProps = structuredConfigProps.getStructured("resource");
assertThat(resourceProps).isNotNull();
List resourceAttributesList =
diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java
index a19dbae578..6a07b2cbb1 100644
--- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java
+++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java
@@ -81,7 +81,21 @@ public final class PeriodicMetricReader implements MetricReader {
@Override
public CompletableResultCode forceFlush() {
- return scheduled.doRun();
+ CompletableResultCode result = new CompletableResultCode();
+ CompletableResultCode doRunResult = scheduled.doRun();
+ doRunResult.whenComplete(
+ () -> {
+ CompletableResultCode flushResult = exporter.flush();
+ flushResult.whenComplete(
+ () -> {
+ if (doRunResult.isSuccess() && flushResult.isSuccess()) {
+ result.succeed();
+ } else {
+ result.fail();
+ }
+ });
+ });
+ return result;
}
@Override
diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java
index 03c8d05cfa..c9093d104f 100644
--- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java
+++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java
@@ -154,6 +154,35 @@ class PeriodicMetricReaderTest {
}
}
+ @Test
+ void forceflush_callsFlush() {
+ MetricExporter metricExporter = mock(MetricExporter.class);
+ when(metricExporter.export(any()))
+ .thenReturn(CompletableResultCode.ofSuccess())
+ .thenReturn(CompletableResultCode.ofSuccess())
+ .thenThrow(new RuntimeException("Export Failed!"));
+ when(metricExporter.flush())
+ .thenReturn(CompletableResultCode.ofSuccess())
+ .thenReturn(CompletableResultCode.ofFailure())
+ .thenReturn(CompletableResultCode.ofSuccess());
+ when(metricExporter.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
+
+ PeriodicMetricReader reader =
+ PeriodicMetricReader.builder(metricExporter)
+ .setInterval(Duration.ofNanos(Long.MAX_VALUE))
+ .build();
+
+ try {
+ reader.register(collectionRegistration);
+ assertThat(reader.forceFlush().join(10, TimeUnit.SECONDS).isSuccess()).isTrue();
+ assertThat(reader.forceFlush().join(10, TimeUnit.SECONDS).isSuccess()).isFalse();
+ assertThat(reader.forceFlush().join(10, TimeUnit.SECONDS).isSuccess()).isFalse();
+ } finally {
+ reader.shutdown();
+ }
+ verify(metricExporter, times(3)).flush();
+ }
+
@Test
@Timeout(2)
@SuppressLogger(PeriodicMetricReader.class)
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 4752f7a31b..58659284e9 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,6 +1,6 @@
pluginManagement {
plugins {
- id("com.gradleup.shadow") version "8.3.6"
+ id("com.gradleup.shadow") version "8.3.7"
id("com.gradle.develocity") version "4.0.2"
id("de.undercouch.download") version "5.6.0"
id("org.jsonschema2pojo") version "1.2.2"