package com.github.kristofa.brave.grpc;
import static com.github.kristofa.brave.IdConversion.convertToLong;
import static com.github.kristofa.brave.grpc.GrpcKeys.GRPC_STATUS_CODE;
import static com.google.common.base.Preconditions.checkNotNull;
import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.KeyValueAnnotation;
import com.github.kristofa.brave.ServerRequestAdapter;
import com.github.kristofa.brave.ServerRequestInterceptor;
import com.github.kristofa.brave.ServerResponseAdapter;
import com.github.kristofa.brave.ServerResponseInterceptor;
import com.github.kristofa.brave.SpanId;
import com.github.kristofa.brave.TraceData;
import com.github.kristofa.brave.internal.Util;
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import io.grpc.Status.Code;
import java.util.Collection;
import java.util.Collections;
/**
* @deprecated Replaced by {@code GrpcTracing} from brave-instrumentation-grpc
*/
@Deprecated
public final class BraveGrpcServerInterceptor implements ServerInterceptor {
/** Creates a tracing interceptor with defaults. Use {@link #builder(Brave)} to customize. */
public static BraveGrpcServerInterceptor create(Brave brave) {
return new Builder(brave).build();
}
public static Builder builder(Brave brave) {
return new Builder(brave);
}
public static final class Builder {
final Brave brave;
Builder(Brave brave) { // intentionally hidden
this.brave = Util.checkNotNull(brave, "brave");
}
public BraveGrpcServerInterceptor build() {
return new BraveGrpcServerInterceptor(this);
}
}
private final ServerRequestInterceptor serverRequestInterceptor;
private final ServerResponseInterceptor serverResponseInterceptor;
BraveGrpcServerInterceptor(Builder b) { // intentionally hidden
this.serverRequestInterceptor = b.brave.serverRequestInterceptor();
this.serverResponseInterceptor = b.brave.serverResponseInterceptor();
}
/**
* @deprecated please use {@link #create(Brave)} or {@link #builder(Brave)}
*/
@Deprecated
public BraveGrpcServerInterceptor(Brave brave) {
this(builder(brave));
}
@Override
public <ReqT, RespT> Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata requestHeaders,
final ServerCallHandler<ReqT, RespT> next) {
return next.startCall(new SimpleForwardingServerCall<ReqT, RespT>(call) {
@Override
public void request(int numMessages) {
serverRequestInterceptor.handle(new GrpcServerRequestAdapter<>(call, requestHeaders));
super.request(numMessages);
}
@Override
public void close(Status status, Metadata trailers) {
serverResponseInterceptor.handle(new GrpcServerResponseAdapter(status));
super.close(status, trailers);
}
}, requestHeaders);
}
static final class GrpcServerRequestAdapter<ReqT, RespT> implements ServerRequestAdapter {
private final ServerCall<ReqT, RespT> call;
private final MethodDescriptor<ReqT, RespT> method;
private final Metadata requestHeaders;
GrpcServerRequestAdapter(ServerCall<ReqT, RespT> call, Metadata requestHeaders) {
this.call = checkNotNull(call);
this.method = checkNotNull(call.getMethodDescriptor());
this.requestHeaders = checkNotNull(requestHeaders);
}
@Override
public TraceData getTraceData() {
String sampled = requestHeaders.get(BravePropagationKeys.Sampled);
String parentSpanId = requestHeaders.get(BravePropagationKeys.ParentSpanId);
String traceId = requestHeaders.get(BravePropagationKeys.TraceId);
String spanId = requestHeaders.get(BravePropagationKeys.SpanId);
// Official sampled value is 1, though some old instrumentation send true
Boolean parsedSampled = sampled != null
? sampled.equals("1") || sampled.equalsIgnoreCase("true")
: null;
if (traceId != null && spanId != null) {
return TraceData.create(getSpanId(traceId, spanId, parentSpanId, parsedSampled));
} else if (parsedSampled == null) {
return TraceData.EMPTY;
} else if (parsedSampled.booleanValue()) {
// Invalid: The caller requests the trace to be sampled, but didn't pass IDs
return TraceData.EMPTY;
} else {
return TraceData.NOT_SAMPLED;
}
}
@Override
public String getSpanName() {
return method.getFullMethodName().toLowerCase();
}
@Override
public Collection<KeyValueAnnotation> requestAnnotations() {
// the remote-addr attribute was removed in grpc 1.2
return Collections.emptyList();
}
}
static final class GrpcServerResponseAdapter implements ServerResponseAdapter {
final Status status;
public GrpcServerResponseAdapter(Status status) {
this.status = status;
}
@Override
@SuppressWarnings("unchecked")
public Collection<KeyValueAnnotation> responseAnnotations() {
Code statusCode = status.getCode();
return statusCode == Code.OK
? Collections.<KeyValueAnnotation>emptyList()
: Collections.singletonList(KeyValueAnnotation.create(GRPC_STATUS_CODE, statusCode.name()));
}
}
static SpanId getSpanId(String traceId, String spanId, String parentSpanId, Boolean sampled) {
return SpanId.builder()
.traceIdHigh(traceId.length() == 32 ? convertToLong(traceId, 0) : 0)
.traceId(convertToLong(traceId))
.spanId(convertToLong(spanId))
.sampled(sampled)
.parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build();
}
}