Skip to content
Open
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
17 changes: 3 additions & 14 deletions exporters/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ val testJavaVersion: String? by project

testing {
suites {
register<JvmTestSuite>("testHttpSenderProvider") {
register<JvmTestSuite>("testSenderProvider") {
dependencies {
implementation(project(":exporters:sender:jdk"))
implementation(project(":exporters:sender:okhttp"))
implementation(project(":exporters:sender:grpc-managed-channel"))
}
targets {
all {
Expand All @@ -98,18 +99,6 @@ testing {
}
}
}
suites {
register<JvmTestSuite>("testGrpcSenderProvider") {
dependencies {
implementation(project(":exporters:sender:okhttp"))
implementation(project(":exporters:sender:grpc-managed-channel"))

implementation("io.grpc:grpc-stub")
implementation("io.grpc:grpc-netty")
implementation("com.fasterxml.jackson.core:jackson-core")
}
}
}
suites {
register<JvmTestSuite>("testWithoutUnsafe") {}
}
Expand All @@ -131,7 +120,7 @@ tasks {
}

afterEvaluate {
tasks.named<JavaCompile>("compileTestHttpSenderProviderJava") {
tasks.named<JavaCompile>("compileTestSenderProviderJava") {
options.release.set(11)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.internal;

import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.common.export.GrpcSenderProvider;
import io.opentelemetry.sdk.common.export.HttpSenderProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Utilities for loading senders.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class SenderUtil {

private static final Logger LOGGER = Logger.getLogger(SenderUtil.class.getName());

private static final String OLD_GRPC_SPI_PROPERTY =
"io.opentelemetry.exporter.internal.grpc.GrpcSenderProvider";
private static final String GRPC_SPI_PROPERTY =
"io.opentelemetry.sdk.common.export.GrpcSenderProvider";
private static final String OLD_HTTP_SPI_PROPERTY =
"io.opentelemetry.exporter.internal.http.HttpSenderProvider";
private static final String HTTP_SPI_PROPERTY =
"io.opentelemetry.sdk.common.export.HttpSenderProvider";

private SenderUtil() {}

/**
* Resolve the {@link GrpcSenderProvider}.
*
* <p>If no {@link GrpcSenderProvider} is available, throw {@link IllegalStateException}.
*
* <p>If only one {@link GrpcSenderProvider} is available, use it.
*
* <p>If multiple are available and..
*
* <ul>
* <li>{@code io.opentelemetry.sdk.common.export.GrpcSenderProvider} is empty, use the first
* found.
* <li>{@code io.opentelemetry.sdk.common.export.GrpcSenderProvider} is set, use the matching
* provider. If none match, throw {@link IllegalStateException}.
* </ul>
*/
public static GrpcSenderProvider resolveGrpcSenderProvider(ComponentLoader componentLoader) {
Map<String, GrpcSenderProvider> grpcSenderProviders = new HashMap<>();
for (GrpcSenderProvider spi : componentLoader.load(GrpcSenderProvider.class)) {
grpcSenderProviders.put(spi.getClass().getName(), spi);
}

// No provider on classpath, throw
if (grpcSenderProviders.isEmpty()) {
throw new IllegalStateException(
"No GrpcSenderProvider found on classpath. Please add dependency on "
+ "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-grpc-managed-channel");
}

// Exactly one provider on classpath, use it
if (grpcSenderProviders.size() == 1) {
return grpcSenderProviders.values().stream().findFirst().get();
}

// If we've reached here, there are multiple GrpcSenderProviders
String configuredSender = ConfigUtil.getString(GRPC_SPI_PROPERTY, "");
// TODO: remove support for reading OLD_SPI_PROPERTY after 1.61.0
if (configuredSender.isEmpty()) {
configuredSender = ConfigUtil.getString(OLD_GRPC_SPI_PROPERTY, "");
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
OLD_GRPC_SPI_PROPERTY
+ " was used to set GrpcSenderProvider. Please use "
+ GRPC_SPI_PROPERTY
+ " instead. "
+ OLD_GRPC_SPI_PROPERTY
+ " will be removed after 1.61.0");
}
Comment on lines +77 to +86
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic error in warning condition: The warning message says "was used to set GrpcSenderProvider" but the condition is actually checking if configuredSender.isEmpty() after reading from OLD_GRPC_SPI_PROPERTY. This means the warning will be logged when the old property is NOT set, which is incorrect. The condition should be !configuredSender.isEmpty() to log the warning when the old property was actually used.

Copilot uses AI. Check for mistakes.
}

// Multiple providers but none configured, use first we find and log a warning
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
"Multiple GrpcSenderProvider found. Please include only one, "
+ "or specify preference setting "
+ GRPC_SPI_PROPERTY
+ " to the FQCN of the preferred provider.");
return grpcSenderProviders.values().stream().findFirst().get();
}

// Multiple providers with configuration match, use configuration match
if (grpcSenderProviders.containsKey(configuredSender)) {
return grpcSenderProviders.get(configuredSender);
}

// Multiple providers, configured does not match, throw
throw new IllegalStateException(
"No GrpcSenderProvider matched configured " + GRPC_SPI_PROPERTY + ": " + configuredSender);
}

/**
* Resolve the {@link HttpSenderProvider}.
*
* <p>If no {@link HttpSenderProvider} is available, throw {@link IllegalStateException}.
*
* <p>If only one {@link HttpSenderProvider} is available, use it.
*
* <p>If multiple are available and..
*
* <ul>
* <li>{@code io.opentelemetry.sdk.common.export.HttpSenderProvider} is empty, use the first
* found.
* <li>{@code io.opentelemetry.sdk.common.export.HttpSenderProvider} is set, use the matching
* provider. If none match, throw {@link IllegalStateException}.
* </ul>
*/
public static HttpSenderProvider resolveHttpSenderProvider(ComponentLoader componentLoader) {
Map<String, HttpSenderProvider> httpSenderProviders = new HashMap<>();
for (HttpSenderProvider spi : componentLoader.load(HttpSenderProvider.class)) {
httpSenderProviders.put(spi.getClass().getName(), spi);
}

// No provider on classpath, throw
if (httpSenderProviders.isEmpty()) {
throw new IllegalStateException(
"No HttpSenderProvider found on classpath. Please add dependency on "
+ "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-jdk");
}

// Exactly one provider on classpath, use it
if (httpSenderProviders.size() == 1) {
return httpSenderProviders.values().stream().findFirst().get();
}

// If we've reached here, there are multiple HttpSenderProviders
String configuredSender = ConfigUtil.getString(HTTP_SPI_PROPERTY, "");
// TODO: remove support for reading OLD_SPI_PROPERTY after 1.61.0
if (configuredSender.isEmpty()) {
configuredSender = ConfigUtil.getString(OLD_HTTP_SPI_PROPERTY, "");
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
OLD_HTTP_SPI_PROPERTY
+ " was used to set HttpSenderProvider. Please use "
+ HTTP_SPI_PROPERTY
+ " instead. "
+ OLD_HTTP_SPI_PROPERTY
+ " will be removed after 1.61.0");
}
Comment on lines +149 to +158
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic error in warning condition: The warning message says "was used to set HttpSenderProvider" but the condition is actually checking if configuredSender.isEmpty() after reading from OLD_HTTP_SPI_PROPERTY. This means the warning will be logged when the old property is NOT set, which is incorrect. The condition should be !configuredSender.isEmpty() to log the warning when the old property was actually used.

Copilot uses AI. Check for mistakes.
}

// Multiple providers but none configured, use first we find and log a warning
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
"Multiple HttpSenderProvider found. Please include only one, "
+ "or specify preference setting "
+ HTTP_SPI_PROPERTY
+ " to the FQCN of the preferred provider.");
return httpSenderProviders.values().stream().findFirst().get();
}

// Multiple providers with configuration match, use configuration match
if (httpSenderProviders.containsKey(configuredSender)) {
return httpSenderProviders.get(configuredSender);
}

// Multiple providers, configured does not match, throw
throw new IllegalStateException(
"No HttpSenderProvider matched configured " + HTTP_SPI_PROPERTY + ": " + configuredSender);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

package io.opentelemetry.exporter.internal.grpc;

import static io.opentelemetry.exporter.internal.SenderUtil.resolveGrpcSenderProvider;

import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
Expand Down Expand Up @@ -49,10 +50,6 @@ public class GrpcExporterBuilder {
public static final long DEFAULT_CONNECT_TIMEOUT_SECS = 10;

private static final Logger LOGGER = Logger.getLogger(GrpcExporterBuilder.class.getName());
private static final String OLD_SPI_PROPERTY =
"io.opentelemetry.exporter.internal.grpc.GrpcSenderProvider";
private static final String SPI_PROPERTY =
"io.opentelemetry.sdk.common.export.GrpcSenderProvider";

private final StandardComponentId.ExporterType exporterType;
private final String fullMethodName;
Expand Down Expand Up @@ -219,7 +216,7 @@ public GrpcExporter build() {
};

boolean isPlainHttp = "http".equals(endpoint.getScheme());
GrpcSenderProvider grpcSenderProvider = resolveGrpcSenderProvider();
GrpcSenderProvider grpcSenderProvider = resolveGrpcSenderProvider(componentLoader);
GrpcSender grpcSender =
grpcSenderProvider.createSender(
ImmutableGrpcSenderConfig.create(
Expand Down Expand Up @@ -284,76 +281,4 @@ public String toString(boolean includePrefixAndSuffix) {
public String toString() {
return toString(true);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just moving this code to a more neutral SenderUtil class since its now being used outside GrpcExporterBuilder.

/**
* Resolve the {@link GrpcSenderProvider}.
*
* <p>If no {@link GrpcSenderProvider} is available, throw {@link IllegalStateException}.
*
* <p>If only one {@link GrpcSenderProvider} is available, use it.
*
* <p>If multiple are available and..
*
* <ul>
* <li>{@code io.opentelemetry.sdk.common.export.GrpcSenderProvider} is empty, use the first
* found.
* <li>{@code io.opentelemetry.sdk.common.export.GrpcSenderProvider} is set, use the matching
* provider. If none match, throw {@link IllegalStateException}.
* </ul>
*/
private GrpcSenderProvider resolveGrpcSenderProvider() {
Map<String, GrpcSenderProvider> grpcSenderProviders = new HashMap<>();
for (GrpcSenderProvider spi : componentLoader.load(GrpcSenderProvider.class)) {
grpcSenderProviders.put(spi.getClass().getName(), spi);
}

// No provider on classpath, throw
if (grpcSenderProviders.isEmpty()) {
throw new IllegalStateException(
"No GrpcSenderProvider found on classpath. Please add dependency on "
+ "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-grpc-managed-channel");
}

// Exactly one provider on classpath, use it
if (grpcSenderProviders.size() == 1) {
return grpcSenderProviders.values().stream().findFirst().get();
}

// If we've reached here, there are multiple GrpcSenderProviders
String configuredSender = ConfigUtil.getString(SPI_PROPERTY, "");
// TODO: remove support for reading OLD_SPI_PROPERTY after 1.61.0
if (configuredSender.isEmpty()) {
configuredSender = ConfigUtil.getString(OLD_SPI_PROPERTY, "");
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
OLD_SPI_PROPERTY
+ " was used to set GrpcSenderProvider. Please use "
+ SPI_PROPERTY
+ " instead. "
+ OLD_SPI_PROPERTY
+ " will be removed after 1.61.0");
}
}

// Multiple providers but none configured, use first we find and log a warning
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
"Multiple GrpcSenderProvider found. Please include only one, "
+ "or specify preference setting "
+ SPI_PROPERTY
+ " to the FQCN of the preferred provider.");
return grpcSenderProviders.values().stream().findFirst().get();
}

// Multiple providers with configuration match, use configuration match
if (grpcSenderProviders.containsKey(configuredSender)) {
return grpcSenderProviders.get(configuredSender);
}

// Multiple providers, configured does not match, throw
throw new IllegalStateException(
"No GrpcSenderProvider matched configured " + SPI_PROPERTY + ": " + configuredSender);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
@AutoValue
abstract class ImmutableGrpcSenderConfig implements ExtendedGrpcSenderConfig {
public abstract class ImmutableGrpcSenderConfig implements ExtendedGrpcSenderConfig {

@SuppressWarnings("TooManyParameters")
static ImmutableGrpcSenderConfig create(
public static ImmutableGrpcSenderConfig create(
URI endpoint,
String fullMethodName,
@Nullable Compressor compressor,
Expand Down
Loading
Loading