Skip to content

Refactor JaegerRemoteSampler to leverage senders#8046

Open
jack-berg wants to merge 1 commit intoopen-telemetry:mainfrom
jack-berg:refactor-jaeger-remote-sampler
Open

Refactor JaegerRemoteSampler to leverage senders#8046
jack-berg wants to merge 1 commit intoopen-telemetry:mainfrom
jack-berg:refactor-jaeger-remote-sampler

Conversation

@jack-berg
Copy link
Member

Contributes to #8001 by refactoring JaegerRemoteSampler to be built on top of the newly public sender APIs.

This simplifies the code, and allows JaegerRemoteSampler to be able to automatically take advantage of breaking out opentelemetry-exporter-sender-okhttp into separate versions for okhttp 4.x and 5.x.

@jack-berg jack-berg requested a review from a team as a code owner February 4, 2026 22:57
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.

import org.junit.jupiter.api.extension.RegisterExtension;
import org.junitpioneer.jupiter.SetSystemProperty;

class GrpcExporterTest {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to SenderUtilTest

return new byte[] {}; // TODO: drain input to byte array
public byte[] parse(InputStream inputStream) {
try {
int bufLen = 4 * 0x400; // 4KB
Copy link
Member Author

Choose a reason for hiding this comment

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

Lifted from the now-deleted MarshallerRemoteSamplerServiceGrpc.

}));
}

private static byte[] getResponseMessageBytes(byte[] bodyBytes) throws IOException {
Copy link
Member Author

Choose a reason for hiding this comment

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

Lifted from the now-deleted OkHttpGrpcService

@codecov
Copy link

codecov bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 84.74576% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.25%. Comparing base (d31d468) to head (a5f73ba).

Files with missing lines Patch % Lines
...pc/managedchannel/internal/UpstreamGrpcSender.java 44.44% 9 Missing and 1 partial ⚠️
...orter/sender/okhttp/internal/OkHttpGrpcSender.java 57.14% 5 Missing and 1 partial ⚠️
...sion/trace/jaeger/sampler/JaegerRemoteSampler.java 93.33% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8046      +/-   ##
============================================
+ Coverage     90.17%   90.25%   +0.08%     
+ Complexity     7479     7452      -27     
============================================
  Files           834      832       -2     
  Lines         22559    22432     -127     
  Branches       2239     2222      -17     
============================================
- Hits          20342    20246      -96     
+ Misses         1515     1492      -23     
+ Partials        702      694       -8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors JaegerRemoteSampler to use the public sender APIs (GrpcSender and GrpcSenderProvider) instead of maintaining its own internal HTTP client implementations. This change addresses issue #8001 regarding OkHttp compatibility, allowing the sampler to automatically benefit from separate versions of opentelemetry-exporter-sender-okhttp for OkHttp 4.x and 5.x.

Changes:

  • Replaced internal OkHttpGrpcService and UpstreamGrpcService classes with the public GrpcSender API
  • Converted JaegerRemoteSampler from synchronous to asynchronous callback-based communication
  • Extracted common sender resolution logic into a new SenderUtil class shared between GrpcExporterBuilder and HttpExporterBuilder

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
JaegerRemoteSampler.java Refactored to use GrpcSender with async callbacks instead of synchronous GrpcService execution
JaegerRemoteSamplerBuilder.java Updated to resolve and create GrpcSender using GrpcSenderProvider instead of building custom HTTP clients
OkHttpGrpcService.java Deleted - functionality moved to OkHttpGrpcSender
UpstreamGrpcService.java Deleted - functionality exists in UpstreamGrpcSender
GrpcService.java Deleted - replaced by public GrpcSender interface
MarshallerRemoteSamplerServiceGrpc.java Deleted - no longer needed with sender abstraction
SamplingStrategyResponseUnMarshaler.java Simplified to static read() method, removing stateful instance pattern
OkHttpGrpcSender.java Added response message extraction logic from deleted OkHttpGrpcService
UpstreamGrpcSender.java Added input stream parsing implementation for response messages
SenderUtil.java New utility class consolidating sender provider resolution logic
GrpcExporterBuilder.java Refactored to delegate sender resolution to SenderUtil
HttpExporterBuilder.java Refactored to delegate sender resolution to SenderUtil
ManagedChannelUtil.java Removed shutdownChannel() method (now handled by senders)
ImmutableGrpcSenderConfig.java Made public to support external usage by JaegerRemoteSamplerBuilder
build.gradle.kts (jaeger-remote-sampler) Removed direct OkHttp dependency, added test configuration for GrpcSenderProvider
build.gradle.kts (exporters/common) Reorganized test suites to consolidate sender provider tests
Test files Updated to reflect field name changes (delegategrpcSender) and use GrpcStatusCode enum

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

default:
logger.log(
Level.WARNING,
"Failed to export "
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.

Inconsistent error message: "Failed to export" should be "Failed to execute" to match the UNIMPLEMENTED and UNAVAILABLE cases (lines 96 and 105) and maintain consistency with the previous implementation.

Suggested change
"Failed to export "
"Failed to execute "

Copilot uses AI. Check for mistakes.
private static void onError(Throwable e) {
logger.log(
Level.SEVERE,
"Failed to export "
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.

Inconsistent error message: "Failed to export" should be "Failed to execute" to maintain consistency with the message patterns in the rest of the method and with the previous implementation.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +86
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");
}
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.
Comment on lines +149 to +158
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");
}
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.
Comment on lines +78 to +123
private void onResponse(GrpcResponse grpcResponse) {
GrpcStatusCode statusCode = grpcResponse.getStatusCode();

if (statusCode == GrpcStatusCode.OK) {
try {
SamplingStrategyResponse strategyResponse =
SamplingStrategyResponseUnMarshaler.read(grpcResponse.getResponseMessage());
sampler = updateSampler(strategyResponse);
} catch (IOException e) {
logger.log(Level.WARNING, "Failed to unmarshal strategy response", e);
}
} catch (Throwable e) { // keep the timer thread alive
logger.log(Level.WARNING, "Failed to update sampler", e);
return;
}

switch (statusCode) {
case UNIMPLEMENTED:
logger.log(
Level.SEVERE,
"Failed to execute "
+ type
+ "s. Server responded with UNIMPLEMENTED. "
+ "Full error message: "
+ grpcResponse.getStatusDescription());
break;
case UNAVAILABLE:
logger.log(
Level.SEVERE,
"Failed to execute "
+ type
+ "s. Server is UNAVAILABLE. "
+ "Make sure your service is running and reachable from this network. "
+ "Full error message:"
+ grpcResponse.getStatusDescription());
break;
default:
logger.log(
Level.WARNING,
"Failed to export "
+ type
+ "s. Server responded with gRPC status code "
+ statusCode.name()
+ ". Error message: "
+ grpcResponse.getStatusDescription());
break;
}
}
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.

Missing exception handling in async callbacks: The previous synchronous implementation had a broad catch (Throwable e) block to prevent exceptions from terminating the scheduled polling thread. With the new asynchronous design, if an uncaught exception occurs in the onResponse callback (e.g., from updateSampler), it could propagate to the underlying HTTP client's thread pool and potentially cause issues. Consider wrapping the entire callback body in a try-catch block to match the previous behavior and ensure robustness.

Copilot uses AI. Check for mistakes.
+ type
+ "s. Server is UNAVAILABLE. "
+ "Make sure your service is running and reachable from this network. "
+ "Full error message:"
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.

Inconsistent spacing in error message: Line 109 is missing a space before the colon ("Full error message:" vs "Full error message: " on line 99). This inconsistency makes the error message formatting differ between error cases.

Suggested change
+ "Full error message:"
+ "Full error message: "

Copilot uses AI. Check for mistakes.
private static byte[] getResponseMessageBytes(byte[] bodyBytes) throws IOException {
if (bodyBytes.length > 5) {
ByteArrayInputStream bodyStream = new ByteArrayInputStream(bodyBytes);
bodyStream.skip(5);
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.

Method getResponseMessageBytes ignores exceptional return value of ByteArrayInputStream.skip.

Suggested change
bodyStream.skip(5);
long skipped = bodyStream.skip(5);
if (skipped != 5) {
throw new IOException("Invalid response");
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant