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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.api.gax.rpc.LibraryMetadata;
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
Expand All @@ -48,12 +49,12 @@
@InternalApi
public class SpanTracerFactory implements ApiTracerFactory {
private final Tracer tracer;

private final OpenTelemetry openTelemetry;
private final ApiTracerContext apiTracerContext;

/** Creates a SpanTracerFactory */
public SpanTracerFactory(OpenTelemetry openTelemetry) {
this(openTelemetry.getTracer("gax-java"), ApiTracerContext.empty());
this(openTelemetry, null, ApiTracerContext.empty());
}

/**
Expand All @@ -62,13 +63,18 @@ public SpanTracerFactory(OpenTelemetry openTelemetry) {
* internally.
*/
@VisibleForTesting
SpanTracerFactory(Tracer tracer, ApiTracerContext apiTracerContext) {
SpanTracerFactory(OpenTelemetry openTelemetry, Tracer tracer, ApiTracerContext apiTracerContext) {
this.openTelemetry = openTelemetry;
this.tracer = tracer;
this.apiTracerContext = apiTracerContext;
}

@Override
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
if (tracer == null) {
// Return a no-op tracer if withContext hasn't been called to initialize the tracer properly
return new BaseApiTracer();
}
// TODO(diegomarquezp): this is a placeholder for span names and will be adjusted as the
// feature is developed.
String attemptSpanName = spanName.getClientName() + "/" + spanName.getMethodName() + "/attempt";
Expand All @@ -78,6 +84,10 @@ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType op

@Override
public ApiTracer newTracer(ApiTracer parent, ApiTracerContext apiTracerContext) {
if (tracer == null) {
// Return a no-op tracer if withContext hasn't been called to initialize the tracer properly
return new BaseApiTracer();
}
ApiTracerContext mergedContext = this.apiTracerContext.merge(apiTracerContext);
return new SpanTracer(tracer, mergedContext);
}
Expand All @@ -87,8 +97,15 @@ public ApiTracerContext getApiTracerContext() {
return apiTracerContext;
}

/**
* Returns a new SpanTracerFactory with the provided context. The Tracer is re-initialized using
* the artifact name and version from the library metadata.
*/
@Override
public ApiTracerFactory withContext(ApiTracerContext context) {
return new SpanTracerFactory(tracer, apiTracerContext.merge(context));
ApiTracerContext mergedContext = this.apiTracerContext.merge(context);
LibraryMetadata metadata = mergedContext.libraryMetadata();
Tracer newTracer = openTelemetry.getTracer(metadata.artifactName(), metadata.version());
return new SpanTracerFactory(openTelemetry, newTracer, mergedContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand All @@ -41,6 +42,7 @@
import com.google.api.gax.rpc.LibraryMetadata;
import com.google.api.gax.tracing.ApiTracerContext.Transport;
import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
Expand All @@ -54,15 +56,19 @@
import org.mockito.ArgumentCaptor;

class SpanTracerFactoryTest {
private OpenTelemetry openTelemetry;
private Tracer tracer;
private SpanBuilder spanBuilder;
private Span span;

@BeforeEach
void setUp() {
openTelemetry = mock(OpenTelemetry.class);
tracer = mock(Tracer.class);
spanBuilder = mock(SpanBuilder.class);
span = mock(Span.class);
when(openTelemetry.getTracer(nullable(String.class), nullable(String.class)))
.thenReturn(tracer);
when(tracer.spanBuilder(anyString())).thenReturn(spanBuilder);
when(spanBuilder.setSpanKind(any())).thenReturn(spanBuilder);
when(spanBuilder.setAllAttributes(any(Attributes.class))).thenReturn(spanBuilder);
Expand All @@ -72,7 +78,8 @@ void setUp() {
@ParameterizedTest
@ValueSource(booleans = {false, true})
void testNewTracer_createsSpanTracer(boolean useContext) {
SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracer tracerInstance;
if (useContext) {
ApiTracerContext context =
Expand All @@ -92,7 +99,8 @@ void testNewTracer_createsSpanTracer(boolean useContext) {
@ParameterizedTest
@ValueSource(booleans = {false, true})
void testNewTracer_addsAttributes(boolean useContext) {
ApiTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
ApiTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
factory =
factory.withContext(
ApiTracerContext.newBuilder()
Expand Down Expand Up @@ -132,7 +140,8 @@ void testWithContext_addsInferredAttributes(boolean useContext) {
.setServerAddress("example.com")
.build();

SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracerFactory factoryWithContext = factory.withContext(context);

ApiTracer tracerInstance;
Expand Down Expand Up @@ -166,7 +175,8 @@ void testWithContext_addsInferredAttributes(boolean useContext) {
void testWithContext_noEndpointContext_doesNotAddServerAddressAttribute(boolean useContext) {
ApiTracerContext context = ApiTracerContext.empty();

SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracerFactory factoryWithContext = factory.withContext(context);

ApiTracer tracerInstance;
Expand Down Expand Up @@ -203,7 +213,8 @@ void testNewTracer_withContext_grpc_usesFullMethodName() {
.setLibraryMetadata(LibraryMetadata.empty())
.build();

SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracer tracerInstance = factory.newTracer(null, context);

tracerInstance.attemptStarted(null, 1);
Expand All @@ -229,7 +240,8 @@ void testNewTracer_withContext_http_usesHttpMethodAndPathTemplate(
.setLibraryMetadata(LibraryMetadata.empty())
.build();

SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracer tracerInstance = factory.newTracer(null, context);

tracerInstance.attemptStarted(null, 1);
Expand All @@ -246,7 +258,8 @@ void testNewTracer_withContext_http_noHttpMethodOrPathTemplate_usesFullMethodNam
.setLibraryMetadata(LibraryMetadata.empty())
.build();

SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracer tracerInstance = factory.newTracer(null, context);

tracerInstance.attemptStarted(null, 1);
Expand All @@ -256,7 +269,8 @@ void testNewTracer_withContext_http_noHttpMethodOrPathTemplate_usesFullMethodNam

@Test
void testNewTracer_withSpanName_usesPlaceholder() {
SpanTracerFactory factory = new SpanTracerFactory(tracer, ApiTracerContext.empty());
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, tracer, ApiTracerContext.empty());
ApiTracer tracerInstance =
factory.newTracer(null, SpanName.of("Service", "Method"), OperationType.Unary);

Expand All @@ -272,7 +286,7 @@ void testNewTracer_mergesFactoryContext() {
.setServerAddress("factory-address")
.setLibraryMetadata(LibraryMetadata.empty())
.build();
SpanTracerFactory factory = new SpanTracerFactory(tracer, apiTracerContext);
SpanTracerFactory factory = new SpanTracerFactory(openTelemetry, tracer, apiTracerContext);

ApiTracerContext callContext =
ApiTracerContext.newBuilder()
Expand All @@ -293,4 +307,19 @@ void testNewTracer_mergesFactoryContext() {
assertThat(attributes.asMap())
.containsEntry(AttributeKey.stringKey("rpc.method"), "Service/Method");
}

@Test
void testNoOpWhenTracerNull() {
SpanTracerFactory factory =
new SpanTracerFactory(openTelemetry, null, ApiTracerContext.empty());

ApiTracer tracerInstance =
factory.newTracer(null, SpanName.of("Service", "Method"), OperationType.Unary);

assertThat(tracerInstance).isInstanceOf(BaseApiTracer.class);

ApiTracer tracerInstance2 = factory.newTracer(null, ApiTracerContext.empty());

assertThat(tracerInstance2).isInstanceOf(BaseApiTracer.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@
import com.google.showcase.v1beta1.EchoClient;
import com.google.showcase.v1beta1.EchoRequest;
import com.google.showcase.v1beta1.EchoSettings;
import com.google.showcase.v1beta1.GetUserRequest;
import com.google.showcase.v1beta1.IdentityClient;
import com.google.showcase.v1beta1.it.util.TestClientInitializer;
import com.google.showcase.v1beta1.stub.EchoStub;
import com.google.showcase.v1beta1.stub.EchoStubSettings;
Expand Down Expand Up @@ -94,24 +92,20 @@ void tearDown() {
}

@Test
void testTracing_successfulIdentityGetUser_grpc() throws Exception {
void testTracing_successfulEcho_grpc() throws Exception {
SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk);

try (IdentityClient client =
TestClientInitializer.createGrpcIdentityClientOpentelemetry(tracingFactory)) {
try (EchoClient client =
TestClientInitializer.createGrpcEchoClientOpentelemetry(tracingFactory)) {

try {
client.getUser(GetUserRequest.newBuilder().setName("users/test-user").build());
} catch (Exception e) {
// Ignored, the showcase server may not have this user, but trace is still generated.
}
client.echo(EchoRequest.newBuilder().setContent("tracing-test").build());

List<SpanData> spans = spanExporter.getFinishedSpanItems();
assertThat(spans).isNotEmpty();

SpanData attemptSpan =
spans.stream()
.filter(span -> span.getName().equals("google.showcase.v1beta1.Identity/GetUser"))
.filter(span -> span.getName().equals("google.showcase.v1beta1.Echo/Echo"))
.findFirst()
.orElseThrow(() -> new AssertionError("Incorrect span name"));
assertThat(attemptSpan.getKind()).isEqualTo(SpanKind.CLIENT);
Expand Down Expand Up @@ -149,46 +143,30 @@ void testTracing_successfulIdentityGetUser_grpc() throws Exception {
attemptSpan
.getAttributes()
.get(AttributeKey.stringKey(ObservabilityAttributes.GRPC_RPC_METHOD_ATTRIBUTE)))
.isEqualTo("google.showcase.v1beta1.Identity/GetUser");
// {x-version-update-start:gapic-showcase:current}
assertThat(
attemptSpan
.getAttributes()
.get(AttributeKey.stringKey(ObservabilityAttributes.VERSION_ATTRIBUTE)))
.isEqualTo("0.0.0-SNAPSHOT");
.isEqualTo("google.showcase.v1beta1.Echo/Echo");
assertThat(attemptSpan.getInstrumentationScopeInfo().getName()).isEqualTo(SHOWCASE_ARTIFACT);
// {x-version-update-end}
assertThat(
attemptSpan
.getAttributes()
.get(
AttributeKey.stringKey(
ObservabilityAttributes.DESTINATION_RESOURCE_ID_ATTRIBUTE)))
.isEqualTo("users/test-user");
}
}

@Test
void testTracing_successfulIdentityGetUser_httpjson() throws Exception {
void testTracing_successfulEcho_httpjson() throws Exception {
SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk);

try (IdentityClient client =
TestClientInitializer.createHttpJsonIdentityClientOpentelemetry(tracingFactory)) {
try (EchoClient client =
TestClientInitializer.createHttpJsonEchoClientOpentelemetry(tracingFactory)) {

try {
client.getUser(GetUserRequest.newBuilder().setName("users/test-user").build());
} catch (Exception e) {
// Ignored, the showcase server may not have this user, but trace is still generated.
}
client.echo(EchoRequest.newBuilder().setContent("tracing-test").build());

List<SpanData> spans = spanExporter.getFinishedSpanItems();
assertThat(spans).isNotEmpty();

SpanData attemptSpan =
spans.stream()
.filter(span -> span.getName().equals("GET v1beta1/{name=users/*}"))
.filter(span -> span.getName().equals("POST v1beta1/echo:echo"))
.findFirst()
.orElseThrow(
() -> new AssertionError("Attempt span 'GET v1beta1/{name=users/*}' not found"));
() -> new AssertionError("Attempt span 'POST v1beta1/echo:echo' not found"));
assertThat(attemptSpan.getKind()).isEqualTo(SpanKind.CLIENT);
assertThat(
attemptSpan
Expand Down Expand Up @@ -219,19 +197,13 @@ void testTracing_successfulIdentityGetUser_httpjson() throws Exception {
attemptSpan
.getAttributes()
.get(AttributeKey.stringKey(ObservabilityAttributes.HTTP_METHOD_ATTRIBUTE)))
.isEqualTo("GET");
.isEqualTo("POST");
assertThat(
attemptSpan
.getAttributes()
.get(AttributeKey.stringKey(ObservabilityAttributes.HTTP_URL_TEMPLATE_ATTRIBUTE)))
.isEqualTo("v1beta1/{name=users/*}");
assertThat(
attemptSpan
.getAttributes()
.get(
AttributeKey.stringKey(
ObservabilityAttributes.DESTINATION_RESOURCE_ID_ATTRIBUTE)))
.isEqualTo("users/test-user");
.isEqualTo("v1beta1/echo:echo");
assertThat(attemptSpan.getInstrumentationScopeInfo().getName()).isEqualTo(SHOWCASE_ARTIFACT);
}
}

Expand Down
Loading