Skip to content
Merged
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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -32,52 +32,87 @@

import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.Tracer;
import java.util.HashMap;
import java.util.Map;

/**
* An implementation of {@link ApiTracer} that uses a {@link TraceManager} to record traces. This
* implementation is agnostic to the specific {@link TraceManager} in order to allow extensions that
* interact with other backends.
*/
/** An implementation of {@link ApiTracer} that uses OpenTelemetry to record traces. */
@BetaApi
@InternalApi
public class SpanTracer implements ApiTracer {
public static final String LANGUAGE_ATTRIBUTE = "gcp.client.language";

public static final String DEFAULT_LANGUAGE = "Java";

private final TraceManager traceManager;
private final Tracer tracer;
private final Map<String, Object> attemptAttributes;
private final String attemptSpanName;
private final ApiTracerContext apiTracerContext;
private TraceManager.Span attemptHandle;
private Span attemptSpan;

/**
* Creates a new instance of {@code SpanTracer}.
*
* @param traceManager the {@link TraceManager} to use for recording spans
* @param tracer the {@link Tracer} to use for recording spans
* @param apiTracerContext the {@link ApiTracerContext} to use for recording spans
*/
public SpanTracer(Tracer tracer, ApiTracerContext apiTracerContext) {
this.tracer = tracer;
this.apiTracerContext = apiTracerContext;
this.attemptSpanName = resolveAttemptSpanName(apiTracerContext);
this.attemptAttributes = new HashMap<>();
buildAttributes();
}

/**
* Creates a new instance of {@code SpanTracer} with an explicitly provided span name.
*
* @param tracer the {@link Tracer} to use for recording spans
* @param apiTracerContext the {@link ApiTracerContext} to use for recording spans
* @param attemptSpanName the name of the individual attempt spans
*/
public SpanTracer(
TraceManager traceManager, ApiTracerContext apiTracerContext, String attemptSpanName) {
this.traceManager = traceManager;
@InternalApi
SpanTracer(Tracer tracer, ApiTracerContext apiTracerContext, String attemptSpanName) {
this.tracer = tracer;
this.attemptSpanName = attemptSpanName;
this.apiTracerContext = apiTracerContext;
this.attemptAttributes = new HashMap<>();
buildAttributes();
}

private static String resolveAttemptSpanName(ApiTracerContext apiTracerContext) {
if (apiTracerContext.transport() == ApiTracerContext.Transport.GRPC) {
// gRPC Uses the full method name as span name.
return apiTracerContext.fullMethodName();
} else if (apiTracerContext.httpMethod() == null
|| apiTracerContext.httpPathTemplate() == null) {
// HTTP method name without necessary components defaults to the full method name
return apiTracerContext.fullMethodName();
} else {
// We construct the span name with HTTP method and path template.
return String.format(
"%s %s", apiTracerContext.httpMethod(), apiTracerContext.httpPathTemplate());
}
}

private void buildAttributes() {
this.attemptAttributes.put(LANGUAGE_ATTRIBUTE, DEFAULT_LANGUAGE);
this.attemptAttributes.putAll(this.apiTracerContext.getAttemptAttributes());
}

@Override
public void attemptStarted(Object request, int attemptNumber) {
Map<String, Object> attemptAttributes = new HashMap<>(this.attemptAttributes);
// Start the specific attempt span with the operation span as parent
this.attemptHandle = traceManager.createSpan(attemptSpanName, attemptAttributes);
SpanBuilder spanBuilder = tracer.spanBuilder(attemptSpanName);

// Attempt spans are of the CLIENT kind
spanBuilder.setSpanKind(SpanKind.CLIENT);

spanBuilder.setAllAttributes(ObservabilityUtils.toOtelAttributes(this.attemptAttributes));

this.attemptSpan = spanBuilder.startSpan();
}

@Override
Expand All @@ -86,9 +121,9 @@ public void attemptSucceeded() {
}

private void endAttempt() {
if (attemptHandle != null) {
attemptHandle.end();
attemptHandle = null;
if (attemptSpan != null) {
attemptSpan.end();
attemptSpan = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,27 @@
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;

/**
* A {@link ApiTracerFactory} to build instances of {@link SpanTracer}.
*
* <p>This class wraps the {@link TraceManager} and pass it to {@link SpanTracer}. It will be used
* to record traces in {@link SpanTracer}.
* <p>This class wraps the {@link Tracer} and pass it to {@link SpanTracer}. It will be used to
* record traces in {@link SpanTracer}.
*
* <p>This class is expected to be initialized once during client initialization.
*/
@BetaApi
@InternalApi
public class SpanTracerFactory implements ApiTracerFactory {
private final TraceManager traceManager;
private final Tracer tracer;

private final ApiTracerContext apiTracerContext;

/** Creates a SpanTracerFactory */
public SpanTracerFactory(TraceManager traceManager) {
this(traceManager, ApiTracerContext.empty());
public SpanTracerFactory(OpenTelemetry openTelemetry) {
this(openTelemetry.getTracer("gax-java"), ApiTracerContext.empty());
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not part of this PR, we want to create a tracer with artifact name and version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

}

/**
Expand All @@ -60,8 +62,8 @@ public SpanTracerFactory(TraceManager traceManager) {
* internally.
*/
@VisibleForTesting
SpanTracerFactory(TraceManager traceManager, ApiTracerContext apiTracerContext) {
this.traceManager = traceManager;
SpanTracerFactory(Tracer tracer, ApiTracerContext apiTracerContext) {
this.tracer = tracer;
this.apiTracerContext = apiTracerContext;
}

Expand All @@ -71,28 +73,13 @@ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType op
// feature is developed.
String attemptSpanName = spanName.getClientName() + "/" + spanName.getMethodName() + "/attempt";

SpanTracer spanTracer = new SpanTracer(traceManager, this.apiTracerContext, attemptSpanName);
return spanTracer;
return new SpanTracer(tracer, this.apiTracerContext, attemptSpanName);
Copy link
Contributor

Choose a reason for hiding this comment

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

Not part of this PR, I think we should reconsider the need of overriding newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) in all factories. Since we don't have all the info in this scenario, I think we should just return a no-op tracer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

}

@Override
public ApiTracer newTracer(ApiTracer parent, ApiTracerContext apiTracerContext) {
ApiTracerContext mergedContext = this.apiTracerContext.merge(apiTracerContext);

String attemptSpanName;
if (mergedContext.transport() == ApiTracerContext.Transport.GRPC) {
// gRPC Uses the full method name as span name.
attemptSpanName = mergedContext.fullMethodName();
} else if (mergedContext.httpMethod() == null || mergedContext.httpPathTemplate() == null) {
// HTTP method name without necessary components defaults to the full method name
attemptSpanName = mergedContext.fullMethodName();
} else {
// We construct the span name with HTTP method and path template.
attemptSpanName =
String.format("%s %s", mergedContext.httpMethod(), mergedContext.httpPathTemplate());
}

return new SpanTracer(traceManager, mergedContext, attemptSpanName);
return new SpanTracer(tracer, mergedContext);
}

@Override
Expand All @@ -102,6 +89,6 @@ public ApiTracerContext getApiTracerContext() {

@Override
public ApiTracerFactory withContext(ApiTracerContext context) {
return new SpanTracerFactory(traceManager, apiTracerContext.merge(context));
return new SpanTracerFactory(tracer, apiTracerContext.merge(context));
}
}

This file was deleted.

Loading
Loading