Split out gRPC library instrumentation. (#1329)

* Split out gRPC library instrumentation.

* Simpler status
This commit is contained in:
Anuraag Agrawal 2020-10-07 17:57:28 +09:00 committed by GitHub
parent 9523f9ffe6
commit 525b3f729d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 448 additions and 358 deletions

View File

@ -5,4 +5,12 @@
package io.opentelemetry.instrumentation.api.tracer; package io.opentelemetry.instrumentation.api.tracer;
public abstract class RpcClientTracer extends BaseTracer {} import io.opentelemetry.trace.Tracer;
public abstract class RpcClientTracer extends BaseTracer {
protected RpcClientTracer() {}
protected RpcClientTracer(Tracer tracer) {
super(tracer);
}
}

View File

@ -9,9 +9,16 @@ import static io.opentelemetry.OpenTelemetry.getPropagators;
import io.grpc.Context; import io.grpc.Context;
import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.trace.Tracer;
public abstract class RpcServerTracer<REQUEST> extends BaseTracer { public abstract class RpcServerTracer<REQUEST> extends BaseTracer {
protected RpcServerTracer() {}
protected RpcServerTracer(Tracer tracer) {
super(tracer);
}
protected abstract TextMapPropagator.Getter<REQUEST> getGetter(); protected abstract TextMapPropagator.Getter<REQUEST> getGetter();
protected <C> Context extract(C carrier, TextMapPropagator.Getter<C> getter) { protected <C> Context extract(C carrier, TextMapPropagator.Getter<C> getter) {

View File

@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply from: "$rootDir/gradle/instrumentation.gradle"
muzzle {
pass {
group = "io.grpc"
module = "grpc-core"
versions = "[1.5.0,)"
}
}
def grpcVersion = '1.5.0'
dependencies {
implementation project(':instrumentation:grpc-1.5:library')
library group: 'io.grpc', name: 'grpc-core', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-netty', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-stub', version: grpcVersion
testImplementation project(':instrumentation:grpc-1.5:testing')
}

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.v1_5;
import io.opentelemetry.javaagent.tooling.Instrumenter;
abstract class AbstractGrpcInstrumentation extends Instrumenter.Default {
public AbstractGrpcInstrumentation() {
super("grpc");
}
@Override
public final String[] helperClassNames() {
return new String[] {
"io.opentelemetry.instrumentation.grpc.v1_5.common.GrpcHelper",
"io.opentelemetry.instrumentation.grpc.v1_5.client.GrpcClientTracer",
"io.opentelemetry.instrumentation.grpc.v1_5.client.GrpcInjectAdapter",
"io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor",
"io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor$TracingClientCall",
"io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor$TracingClientCallListener",
"io.opentelemetry.instrumentation.grpc.v1_5.server.GrpcExtractAdapter",
"io.opentelemetry.instrumentation.grpc.v1_5.server.GrpcServerTracer",
"io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor",
"io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor$TracingServerCall",
"io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor$TracingServerCallListener",
};
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.client; package io.opentelemetry.instrumentation.auto.grpc.v1_5;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass; import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
@ -13,11 +13,8 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.grpc.ClientInterceptor; import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannelBuilder; import io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor;
import io.opentelemetry.instrumentation.auto.api.ContextStore;
import io.opentelemetry.instrumentation.auto.api.InstrumentationContext;
import io.opentelemetry.javaagent.tooling.Instrumenter; import io.opentelemetry.javaagent.tooling.Instrumenter;
import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -26,7 +23,7 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(Instrumenter.class) @AutoService(Instrumenter.class)
public class GrpcClientBuilderBuildInstrumentation extends AbstractGrpcClientInstrumentation { public class GrpcClientBuilderBuildInstrumentation extends AbstractGrpcInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
@ -45,7 +42,6 @@ public class GrpcClientBuilderBuildInstrumentation extends AbstractGrpcClientIns
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void addInterceptor( public static void addInterceptor(
@Advice.This ManagedChannelBuilder thiz,
@Advice.FieldValue("interceptors") List<ClientInterceptor> interceptors) { @Advice.FieldValue("interceptors") List<ClientInterceptor> interceptors) {
boolean shouldRegister = true; boolean shouldRegister = true;
for (ClientInterceptor interceptor : interceptors) { for (ClientInterceptor interceptor : interceptors) {
@ -55,10 +51,7 @@ public class GrpcClientBuilderBuildInstrumentation extends AbstractGrpcClientIns
} }
} }
if (shouldRegister) { if (shouldRegister) {
ContextStore<ManagedChannelBuilder, InetSocketAddress> contextStore = interceptors.add(0, TracingClientInterceptor.newInterceptor());
InstrumentationContext.get(ManagedChannelBuilder.class, InetSocketAddress.class);
InetSocketAddress sockAddr = contextStore.get(thiz);
interceptors.add(0, new TracingClientInterceptor(sockAddr));
} }
} }
} }

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.server; package io.opentelemetry.instrumentation.auto.grpc.v1_5;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
@ -11,6 +11,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.grpc.ServerInterceptor; import io.grpc.ServerInterceptor;
import io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor;
import io.opentelemetry.javaagent.tooling.Instrumenter; import io.opentelemetry.javaagent.tooling.Instrumenter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -20,29 +21,13 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@AutoService(Instrumenter.class) @AutoService(Instrumenter.class)
public class GrpcServerBuilderInstrumentation extends Instrumenter.Default { public class GrpcServerBuilderInstrumentation extends AbstractGrpcInstrumentation {
public GrpcServerBuilderInstrumentation() {
super("grpc", "grpc-server");
}
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.grpc.internal.AbstractServerImplBuilder"); return named("io.grpc.internal.AbstractServerImplBuilder");
} }
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".GrpcServerTracer",
packageName + ".GrpcExtractAdapter",
packageName + ".TracingServerInterceptor",
packageName + ".TracingServerInterceptor$TracingServerCall",
packageName + ".TracingServerInterceptor$TracingServerCallListener",
"io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper",
};
}
@Override @Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() { public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap( return singletonMap(
@ -63,7 +48,7 @@ public class GrpcServerBuilderInstrumentation extends Instrumenter.Default {
} }
} }
if (shouldRegister) { if (shouldRegister) {
interceptors.add(0, TracingServerInterceptor.INSTANCE); interceptors.add(0, TracingServerInterceptor.newInterceptor());
} }
} }
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.v1_5
import io.grpc.ManagedChannelBuilder
import io.grpc.ServerBuilder
import io.opentelemetry.auto.test.AgentTestTrait
import io.opentelemetry.instrumentation.grpc.v1_5.AbstractGrpcStreamingTest
class GrpcStreamingTest extends AbstractGrpcStreamingTest implements AgentTestTrait {
@Override
ServerBuilder configureServer(ServerBuilder server) {
return server
}
@Override
ManagedChannelBuilder configureClient(ManagedChannelBuilder client) {
return client
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.v1_5
import io.grpc.ManagedChannelBuilder
import io.grpc.ServerBuilder
import io.opentelemetry.auto.test.AgentTestTrait
import io.opentelemetry.instrumentation.grpc.v1_5.AbstractGrpcTest
class GrpcTest extends AbstractGrpcTest implements AgentTestTrait {
@Override
ServerBuilder configureServer(ServerBuilder server) {
return server
}
@Override
ManagedChannelBuilder configureClient(ManagedChannelBuilder client) {
return client
}
}

View File

@ -1,54 +0,0 @@
apply from: "$rootDir/gradle/instrumentation.gradle"
apply plugin: 'com.google.protobuf'
apply plugin: 'idea'
muzzle {
pass {
group = "io.grpc"
module = "grpc-core"
versions = "[1.5.0,)"
}
}
buildscript {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
}
}
def grpcVersion = '1.5.0'
protobuf {
protoc {
// Download compiler rather than using locally installed version:
artifact = 'com.google.protobuf:protoc:3.3.0'
}
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}
dependencies {
library group: 'io.grpc', name: 'grpc-core', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-netty', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-stub', version: grpcVersion
testImplementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
// this instrumentation needs to be able to be able to reference the OpenTelemetry API's gRPC Context
// that is shaded in the bootstrap class loader (for sending telemetry to the agent),
// separately from the gRPC Context that is brought by gRPC
compileOnly project(path: ':opentelemetry-api-beta-shaded-for-instrumenting', configuration: 'shadow')
testImplementation project(path: ':opentelemetry-api-beta-shaded-for-instrumenting', configuration: 'shadow')
}

View File

@ -0,0 +1,13 @@
apply from: "$rootDir/gradle/instrumentation-library.gradle"
def grpcVersion = '1.5.0'
dependencies {
library group: 'io.grpc', name: 'grpc-core', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-netty', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion
testLibrary group: 'io.grpc', name: 'grpc-stub', version: grpcVersion
testImplementation project(':instrumentation:grpc-1.5:testing')
}

View File

@ -3,19 +3,25 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.client; package io.opentelemetry.instrumentation.grpc.v1_5.client;
import static io.opentelemetry.trace.Span.Kind.CLIENT; import static io.opentelemetry.trace.Span.Kind.CLIENT;
import io.grpc.Status; import io.grpc.Status;
import io.opentelemetry.instrumentation.api.tracer.RpcClientTracer; import io.opentelemetry.instrumentation.api.tracer.RpcClientTracer;
import io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper; import io.opentelemetry.instrumentation.grpc.v1_5.common.GrpcHelper;
import io.opentelemetry.trace.Span; import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Builder; import io.opentelemetry.trace.Span.Builder;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.attributes.SemanticAttributes; import io.opentelemetry.trace.attributes.SemanticAttributes;
public class GrpcClientTracer extends RpcClientTracer { public class GrpcClientTracer extends RpcClientTracer {
public static final GrpcClientTracer TRACER = new GrpcClientTracer();
protected GrpcClientTracer() {}
protected GrpcClientTracer(Tracer tracer) {
super(tracer);
}
public Span startSpan(String name) { public Span startSpan(String name) {
Builder spanBuilder = tracer.spanBuilder(name).setSpanKind(CLIENT); Builder spanBuilder = tracer.spanBuilder(name).setSpanKind(CLIENT);

View File

@ -3,14 +3,14 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.client; package io.opentelemetry.instrumentation.grpc.v1_5.client;
import io.grpc.Metadata; import io.grpc.Metadata;
import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapPropagator;
public final class GrpcInjectAdapter implements TextMapPropagator.Setter<Metadata> { final class GrpcInjectAdapter implements TextMapPropagator.Setter<Metadata> {
public static final GrpcInjectAdapter SETTER = new GrpcInjectAdapter(); static final GrpcInjectAdapter SETTER = new GrpcInjectAdapter();
@Override @Override
public void set(Metadata carrier, String key, String value) { public void set(Metadata carrier, String key, String value) {

View File

@ -3,84 +3,106 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.client; package io.opentelemetry.instrumentation.grpc.v1_5.client;
import static io.opentelemetry.context.ContextUtils.withScopedContext; import static io.opentelemetry.context.ContextUtils.withScopedContext;
import static io.opentelemetry.instrumentation.auto.grpc.client.GrpcClientTracer.TRACER; import static io.opentelemetry.instrumentation.grpc.v1_5.client.GrpcInjectAdapter.SETTER;
import static io.opentelemetry.instrumentation.auto.grpc.client.GrpcInjectAdapter.SETTER;
import static io.opentelemetry.trace.TracingContextUtils.getSpan;
import static io.opentelemetry.trace.TracingContextUtils.withSpan; import static io.opentelemetry.trace.TracingContextUtils.withSpan;
import io.grpc.CallOptions; import io.grpc.CallOptions;
import io.grpc.Channel; import io.grpc.Channel;
import io.grpc.ClientCall; import io.grpc.ClientCall;
import io.grpc.ClientCall.Listener;
import io.grpc.ClientInterceptor; import io.grpc.ClientInterceptor;
import io.grpc.Context; import io.grpc.Context;
import io.grpc.ForwardingClientCall; import io.grpc.ForwardingClientCall;
import io.grpc.ForwardingClientCallListener; import io.grpc.ForwardingClientCallListener;
import io.grpc.Grpc;
import io.grpc.Metadata; import io.grpc.Metadata;
import io.grpc.MethodDescriptor; import io.grpc.MethodDescriptor;
import io.grpc.Status; import io.grpc.Status;
import io.opentelemetry.OpenTelemetry; import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.common.Attributes; import io.opentelemetry.common.Attributes;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper; import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
import io.opentelemetry.instrumentation.grpc.v1_5.common.GrpcHelper;
import io.opentelemetry.trace.Span; import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.attributes.SemanticAttributes; import io.opentelemetry.trace.attributes.SemanticAttributes;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
public class TracingClientInterceptor implements ClientInterceptor { public class TracingClientInterceptor implements ClientInterceptor {
private final InetSocketAddress peerAddress;
public TracingClientInterceptor(InetSocketAddress peerAddress) { public static ClientInterceptor newInterceptor() {
this.peerAddress = peerAddress; return newInterceptor(new GrpcClientTracer());
}
public static ClientInterceptor newInterceptor(Tracer tracer) {
return newInterceptor(new GrpcClientTracer(tracer));
}
public static ClientInterceptor newInterceptor(GrpcClientTracer tracer) {
return new TracingClientInterceptor(tracer);
}
private final GrpcClientTracer tracer;
private TracingClientInterceptor(GrpcClientTracer tracer) {
this.tracer = tracer;
} }
@Override @Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall( public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) { MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
String methodName = method.getFullMethodName(); String methodName = method.getFullMethodName();
Span span = TRACER.startSpan(methodName); Span span = tracer.startSpan(methodName);
GrpcHelper.prepareSpan(span, methodName);
Context context = withSpan(span, Context.current()); Context context = withSpan(span, Context.current());
final ClientCall<ReqT, RespT> result;
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
GrpcHelper.prepareSpan(span, methodName, peerAddress, false);
ClientCall<ReqT, RespT> result;
try { try {
// call other interceptors // call other interceptors
result = next.newCall(method, callOptions); result = next.newCall(method, callOptions);
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
return new TracingClientCall<>(context, result);
} }
SocketAddress address = result.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
if (address instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) address;
NetPeerUtils.setNetPeer(span, inetSocketAddress);
}
return new TracingClientCall<>(result, span, context, tracer);
} }
static final class TracingClientCall<ReqT, RespT> static final class TracingClientCall<ReqT, RespT>
extends ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT> { extends ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT> {
final Context context;
TracingClientCall(Context context, ClientCall<ReqT, RespT> delegate) { private final Span span;
private final Context context;
private final GrpcClientTracer tracer;
TracingClientCall(
ClientCall<ReqT, RespT> delegate, Span span, Context context, GrpcClientTracer tracer) {
super(delegate); super(delegate);
this.span = span;
this.context = context; this.context = context;
this.tracer = tracer;
} }
@Override @Override
public void start(Listener<RespT> responseListener, Metadata headers) { public void start(Listener<RespT> responseListener, Metadata headers) {
// this reference to io.grpc.Context will be shaded during the build
// see instrumentation.gradle: "relocate OpenTelemetry API dependency usage"
// (luckily the grpc instrumentation doesn't need to reference unshaded grpc Context, so we
// don't need to worry about distinguishing them like in the opentelemetry-api
// instrumentation)
OpenTelemetry.getPropagators().getTextMapPropagator().inject(context, headers, SETTER); OpenTelemetry.getPropagators().getTextMapPropagator().inject(context, headers, SETTER);
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
super.start(new TracingClientCallListener<>(context, responseListener), headers); super.start(
new TracingClientCallListener<>(responseListener, span, context, tracer), headers);
} catch (Throwable e) { } catch (Throwable e) {
Span span = getSpan(context); tracer.endExceptionally(span, e);
TRACER.endExceptionally(span, e);
throw e; throw e;
} }
} }
@ -90,8 +112,7 @@ public class TracingClientInterceptor implements ClientInterceptor {
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
super.sendMessage(message); super.sendMessage(message);
} catch (Throwable e) { } catch (Throwable e) {
Span span = getSpan(context); tracer.endExceptionally(span, e);
TRACER.endExceptionally(span, e);
throw e; throw e;
} }
} }
@ -99,17 +120,22 @@ public class TracingClientInterceptor implements ClientInterceptor {
static final class TracingClientCallListener<RespT> static final class TracingClientCallListener<RespT>
extends ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT> { extends ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT> {
private final Span span;
private final Context context; private final Context context;
private final GrpcClientTracer tracer;
private final AtomicLong messageId = new AtomicLong(); private final AtomicLong messageId = new AtomicLong();
TracingClientCallListener(Context context, ClientCall.Listener<RespT> delegate) { TracingClientCallListener(
Listener<RespT> delegate, Span span, Context context, GrpcClientTracer tracer) {
super(delegate); super(delegate);
this.span = span;
this.context = context; this.context = context;
this.tracer = tracer;
} }
@Override @Override
public void onMessage(RespT message) { public void onMessage(RespT message) {
Span span = getSpan(context);
Attributes attributes = Attributes attributes =
Attributes.of( Attributes.of(
SemanticAttributes.GRPC_MESSAGE_TYPE, SemanticAttributes.GRPC_MESSAGE_TYPE,
@ -119,19 +145,20 @@ public class TracingClientInterceptor implements ClientInterceptor {
span.addEvent("message", attributes); span.addEvent("message", attributes);
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
delegate().onMessage(message); delegate().onMessage(message);
} catch (Throwable e) {
tracer.addThrowable(span, e);
} }
} }
@Override @Override
public void onClose(Status status, Metadata trailers) { public void onClose(Status status, Metadata trailers) {
Span span = getSpan(context);
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
delegate().onClose(status, trailers); delegate().onClose(status, trailers);
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
TRACER.endSpan(span, status); tracer.endSpan(span, status);
} }
@Override @Override
@ -139,8 +166,7 @@ public class TracingClientInterceptor implements ClientInterceptor {
try (Scope ignored = withScopedContext(context)) { try (Scope ignored = withScopedContext(context)) {
delegate().onReady(); delegate().onReady();
} catch (Throwable e) { } catch (Throwable e) {
Span span = getSpan(context); tracer.endExceptionally(span, e);
TRACER.endExceptionally(span, e);
throw e; throw e;
} }
} }

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.grpc.v1_5.common;
import io.grpc.Status.Code;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.StatusCanonicalCode;
import io.opentelemetry.trace.attributes.SemanticAttributes;
public final class GrpcHelper {
public static void prepareSpan(Span span, String fullMethodName) {
int slash = fullMethodName.indexOf('/');
String serviceName = slash == -1 ? fullMethodName : fullMethodName.substring(0, slash);
String methodName = slash == -1 ? null : fullMethodName.substring(slash + 1);
span.setAttribute(SemanticAttributes.RPC_SERVICE, serviceName);
span.setAttribute(SemanticAttributes.RPC_METHOD, methodName);
}
public static StatusCanonicalCode statusFromGrpcStatus(io.grpc.Status grpcStatus) {
return codeFromGrpcCode(grpcStatus.getCode());
}
private static StatusCanonicalCode codeFromGrpcCode(Code grpcCode) {
return grpcCode.equals(Code.OK) ? StatusCanonicalCode.UNSET : StatusCanonicalCode.ERROR;
}
private GrpcHelper() {}
}

View File

@ -3,14 +3,14 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.server; package io.opentelemetry.instrumentation.grpc.v1_5.server;
import io.grpc.Metadata; import io.grpc.Metadata;
import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapPropagator;
public final class GrpcExtractAdapter implements TextMapPropagator.Getter<Metadata> { final class GrpcExtractAdapter implements TextMapPropagator.Getter<Metadata> {
public static final GrpcExtractAdapter GETTER = new GrpcExtractAdapter(); static final GrpcExtractAdapter GETTER = new GrpcExtractAdapter();
@Override @Override
public String get(Metadata carrier, String key) { public String get(Metadata carrier, String key) {

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.server; package io.opentelemetry.instrumentation.grpc.v1_5.server;
import static io.opentelemetry.trace.Span.Kind.SERVER; import static io.opentelemetry.trace.Span.Kind.SERVER;
@ -11,13 +11,19 @@ import io.grpc.Metadata;
import io.grpc.Status; import io.grpc.Status;
import io.opentelemetry.context.propagation.TextMapPropagator.Getter; import io.opentelemetry.context.propagation.TextMapPropagator.Getter;
import io.opentelemetry.instrumentation.api.tracer.RpcServerTracer; import io.opentelemetry.instrumentation.api.tracer.RpcServerTracer;
import io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper; import io.opentelemetry.instrumentation.grpc.v1_5.common.GrpcHelper;
import io.opentelemetry.trace.Span; import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Builder; import io.opentelemetry.trace.Span.Builder;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.attributes.SemanticAttributes; import io.opentelemetry.trace.attributes.SemanticAttributes;
public class GrpcServerTracer extends RpcServerTracer<Metadata> { public class GrpcServerTracer extends RpcServerTracer<Metadata> {
public static final GrpcServerTracer TRACER = new GrpcServerTracer();
GrpcServerTracer() {}
GrpcServerTracer(Tracer tracer) {
super(tracer);
}
public Span startSpan(String name, Metadata headers) { public Span startSpan(String name, Metadata headers) {
Builder spanBuilder = Builder spanBuilder =

View File

@ -3,9 +3,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.server; package io.opentelemetry.instrumentation.grpc.v1_5.server;
import static io.opentelemetry.instrumentation.auto.grpc.server.GrpcServerTracer.TRACER;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith; import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import io.grpc.ForwardingServerCall; import io.grpc.ForwardingServerCall;
@ -13,13 +12,15 @@ import io.grpc.ForwardingServerCallListener;
import io.grpc.Grpc; import io.grpc.Grpc;
import io.grpc.Metadata; import io.grpc.Metadata;
import io.grpc.ServerCall; import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler; import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor; import io.grpc.ServerInterceptor;
import io.grpc.Status; import io.grpc.Status;
import io.opentelemetry.common.Attributes; import io.opentelemetry.common.Attributes;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper; import io.opentelemetry.instrumentation.grpc.v1_5.common.GrpcHelper;
import io.opentelemetry.trace.Span; import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import io.opentelemetry.trace.attributes.SemanticAttributes; import io.opentelemetry.trace.attributes.SemanticAttributes;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -27,20 +28,39 @@ import java.util.concurrent.atomic.AtomicLong;
public class TracingServerInterceptor implements ServerInterceptor { public class TracingServerInterceptor implements ServerInterceptor {
public static final TracingServerInterceptor INSTANCE = new TracingServerInterceptor(); public static ServerInterceptor newInterceptor() {
return newInterceptor(new GrpcServerTracer());
}
private TracingServerInterceptor() {} public static ServerInterceptor newInterceptor(Tracer tracer) {
return newInterceptor(new GrpcServerTracer(tracer));
}
public static ServerInterceptor newInterceptor(GrpcServerTracer tracer) {
return new TracingServerInterceptor(tracer);
}
private final GrpcServerTracer tracer;
private TracingServerInterceptor(GrpcServerTracer tracer) {
this.tracer = tracer;
}
@Override @Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
String methodName = call.getMethodDescriptor().getFullMethodName(); String methodName = call.getMethodDescriptor().getFullMethodName();
Span span = TRACER.startSpan(methodName, headers); Span span = tracer.startSpan(methodName, headers);
SocketAddress addr = call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); SocketAddress address = call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
InetSocketAddress iAddr = addr instanceof InetSocketAddress ? (InetSocketAddress) addr : null; if (address instanceof InetSocketAddress) {
GrpcHelper.prepareSpan(span, methodName, iAddr, true); InetSocketAddress inetSocketAddress = (InetSocketAddress) address;
span.setAttribute(SemanticAttributes.NET_PEER_PORT, inetSocketAddress.getPort());
span.setAttribute(
SemanticAttributes.NET_PEER_IP, inetSocketAddress.getAddress().getHostAddress());
}
GrpcHelper.prepareSpan(span, methodName);
ServerCall.Listener<ReqT> result; ServerCall.Listener<ReqT> result;
try (Scope ignored = currentContextWith(span)) { try (Scope ignored = currentContextWith(span)) {
@ -48,36 +68,39 @@ public class TracingServerInterceptor implements ServerInterceptor {
try { try {
// Wrap the server call so that we can decorate the span // Wrap the server call so that we can decorate the span
// with the resulting status // with the resulting status
TracingServerCall<ReqT, RespT> tracingServerCall = new TracingServerCall<>(span, call); TracingServerCall<ReqT, RespT> tracingServerCall =
new TracingServerCall<>(call, span, tracer);
// call other interceptors // call other interceptors
result = next.startCall(tracingServerCall, headers); result = next.startCall(tracingServerCall, headers);
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
} }
// This ensures the server implementation can see the span in scope // This ensures the server implementation can see the span in scope
return new TracingServerCallListener<>(span, result); return new TracingServerCallListener<>(result, span, tracer);
} }
static final class TracingServerCall<ReqT, RespT> static final class TracingServerCall<ReqT, RespT>
extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> { extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
final Span span; private final Span span;
private final GrpcServerTracer tracer;
TracingServerCall(Span span, ServerCall<ReqT, RespT> delegate) { TracingServerCall(ServerCall<ReqT, RespT> delegate, Span span, GrpcServerTracer tracer) {
super(delegate); super(delegate);
this.span = span; this.span = span;
this.tracer = tracer;
} }
@Override @Override
public void close(Status status, Metadata trailers) { public void close(Status status, Metadata trailers) {
TRACER.setStatus(span, status); tracer.setStatus(span, status);
try (Scope ignored = currentContextWith(span)) { try (Scope ignored = currentContextWith(span)) {
delegate().close(status, trailers); delegate().close(status, trailers);
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
} }
@ -86,11 +109,14 @@ public class TracingServerInterceptor implements ServerInterceptor {
static final class TracingServerCallListener<ReqT> static final class TracingServerCallListener<ReqT>
extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> { extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
private final Span span; private final Span span;
private final GrpcServerTracer tracer;
private final AtomicLong messageId = new AtomicLong(); private final AtomicLong messageId = new AtomicLong();
TracingServerCallListener(Span span, ServerCall.Listener<ReqT> delegate) { TracingServerCallListener(Listener<ReqT> delegate, Span span, GrpcServerTracer tracer) {
super(delegate); super(delegate);
this.span = span; this.span = span;
this.tracer = tracer;
} }
@Override @Override
@ -112,7 +138,7 @@ public class TracingServerInterceptor implements ServerInterceptor {
try (Scope ignored = currentContextWith(span)) { try (Scope ignored = currentContextWith(span)) {
delegate().onHalfClose(); delegate().onHalfClose();
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
} }
@ -123,10 +149,10 @@ public class TracingServerInterceptor implements ServerInterceptor {
delegate().onCancel(); delegate().onCancel();
span.setAttribute("canceled", true); span.setAttribute("canceled", true);
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
TRACER.end(span); tracer.end(span);
} }
@Override @Override
@ -134,10 +160,10 @@ public class TracingServerInterceptor implements ServerInterceptor {
try (Scope ignored = currentContextWith(span)) { try (Scope ignored = currentContextWith(span)) {
delegate().onComplete(); delegate().onComplete();
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
TRACER.end(span); tracer.end(span);
} }
@Override @Override
@ -145,7 +171,7 @@ public class TracingServerInterceptor implements ServerInterceptor {
try (Scope ignored = currentContextWith(span)) { try (Scope ignored = currentContextWith(span)) {
delegate().onReady(); delegate().onReady();
} catch (Throwable e) { } catch (Throwable e) {
TRACER.endExceptionally(span, e); tracer.endExceptionally(span, e);
throw e; throw e;
} }
} }

View File

@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.grpc.v1_5
import io.grpc.ManagedChannelBuilder
import io.grpc.ServerBuilder
import io.opentelemetry.auto.test.InstrumentationTestTrait
import io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor
import io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor
class GrpcStreamingTest extends AbstractGrpcStreamingTest implements InstrumentationTestTrait {
@Override
ServerBuilder configureServer(ServerBuilder server) {
return server.intercept(TracingServerInterceptor.newInterceptor())
}
@Override
ManagedChannelBuilder configureClient(ManagedChannelBuilder client) {
return client.intercept(TracingClientInterceptor.newInterceptor())
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.grpc.v1_5
import io.grpc.ManagedChannelBuilder
import io.grpc.ServerBuilder
import io.opentelemetry.auto.test.InstrumentationTestTrait
import io.opentelemetry.instrumentation.grpc.v1_5.client.TracingClientInterceptor
import io.opentelemetry.instrumentation.grpc.v1_5.server.TracingServerInterceptor
class GrpcTest extends AbstractGrpcTest implements InstrumentationTestTrait {
@Override
ServerBuilder configureServer(ServerBuilder server) {
return server.intercept(TracingServerInterceptor.newInterceptor())
}
@Override
ManagedChannelBuilder configureClient(ManagedChannelBuilder client) {
return client.intercept(TracingClientInterceptor.newInterceptor())
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.auto.grpc.common package io.opentelemetry.instrumentation.grpc.v1_5.common
import io.grpc.Status import io.grpc.Status
import io.opentelemetry.trace.StatusCanonicalCode import io.opentelemetry.trace.StatusCanonicalCode

View File

@ -1,36 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.client;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Map;
abstract class AbstractGrpcClientInstrumentation extends Instrumenter.Default {
public AbstractGrpcClientInstrumentation() {
super("grpc", "grpc-client");
}
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".GrpcClientTracer",
packageName + ".GrpcInjectAdapter",
packageName + ".TracingClientInterceptor",
packageName + ".TracingClientInterceptor$TracingClientCall",
packageName + ".TracingClientInterceptor$TracingClientCallListener",
"io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper",
};
}
@Override
public Map<String, String> contextStore() {
return Collections.singletonMap(
"io.grpc.ManagedChannelBuilder", InetSocketAddress.class.getName());
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.client;
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.grpc.ManagedChannelBuilder;
import io.opentelemetry.instrumentation.auto.api.ContextStore;
import io.opentelemetry.instrumentation.auto.api.InstrumentationContext;
import io.opentelemetry.javaagent.tooling.Instrumenter;
import java.net.InetSocketAddress;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
@AutoService(Instrumenter.class)
public class GrpcClientBuilderForAddressInstrumentation extends AbstractGrpcClientInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(named("io.grpc.ManagedChannelBuilder"));
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod().and(named("forAddress").and(ElementMatchers.takesArguments(2))),
GrpcClientBuilderForAddressInstrumentation.class.getName() + "$ForAddressAdvice");
}
public static class ForAddressAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static final void forAddress(
@Advice.Argument(0) String address,
@Advice.Argument(1) int port,
@Advice.Return ManagedChannelBuilder builder) {
ContextStore<ManagedChannelBuilder, InetSocketAddress> contextStore =
InstrumentationContext.get(ManagedChannelBuilder.class, InetSocketAddress.class);
contextStore.put(builder, InetSocketAddress.createUnresolved(address, port));
}
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.auto.grpc.common;
import io.grpc.Status.Code;
import io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.StatusCanonicalCode;
import io.opentelemetry.trace.attributes.SemanticAttributes;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
public final class GrpcHelper {
private static final Map<Code, StatusCanonicalCode> CODE_MAP;
static {
EnumMap<Code, StatusCanonicalCode> codeMap = new EnumMap<>(Code.class);
codeMap.put(Code.CANCELLED, StatusCanonicalCode.ERROR);
codeMap.put(Code.INVALID_ARGUMENT, StatusCanonicalCode.ERROR);
codeMap.put(Code.DEADLINE_EXCEEDED, StatusCanonicalCode.ERROR);
codeMap.put(Code.NOT_FOUND, StatusCanonicalCode.ERROR);
codeMap.put(Code.ALREADY_EXISTS, StatusCanonicalCode.ERROR);
codeMap.put(Code.PERMISSION_DENIED, StatusCanonicalCode.ERROR);
codeMap.put(Code.RESOURCE_EXHAUSTED, StatusCanonicalCode.ERROR);
codeMap.put(Code.FAILED_PRECONDITION, StatusCanonicalCode.ERROR);
codeMap.put(Code.ABORTED, StatusCanonicalCode.ERROR);
codeMap.put(Code.OUT_OF_RANGE, StatusCanonicalCode.ERROR);
codeMap.put(Code.UNIMPLEMENTED, StatusCanonicalCode.ERROR);
codeMap.put(Code.INTERNAL, StatusCanonicalCode.ERROR);
codeMap.put(Code.UNAVAILABLE, StatusCanonicalCode.ERROR);
codeMap.put(Code.DATA_LOSS, StatusCanonicalCode.ERROR);
codeMap.put(Code.UNAUTHENTICATED, StatusCanonicalCode.ERROR);
codeMap.put(Code.UNKNOWN, StatusCanonicalCode.ERROR);
CODE_MAP = Collections.unmodifiableMap(codeMap);
}
public static void prepareSpan(
Span span, String fullMethodName, InetSocketAddress peerAddress, boolean server) {
int slash = fullMethodName.indexOf('/');
String serviceName = slash == -1 ? fullMethodName : fullMethodName.substring(0, slash);
String methodName = slash == -1 ? null : fullMethodName.substring(slash + 1);
span.setAttribute(SemanticAttributes.RPC_SERVICE, serviceName);
if (methodName != null) {
span.setAttribute(SemanticAttributes.RPC_METHOD, methodName);
}
if (peerAddress != null) {
span.setAttribute(SemanticAttributes.NET_PEER_PORT, (long) peerAddress.getPort());
if (server) {
span.setAttribute(
SemanticAttributes.NET_PEER_IP, peerAddress.getAddress().getHostAddress());
} else {
NetPeerUtils.setNetPeer(span, peerAddress.getHostName(), null);
}
} else {
// The spec says these fields must be populated, so put some values in even if we don't have
// an address recorded.
span.setAttribute(SemanticAttributes.NET_PEER_PORT, 0L);
NetPeerUtils.setNetPeer(span, "(unknown)", null);
}
}
public static StatusCanonicalCode statusFromGrpcStatus(io.grpc.Status grpcStatus) {
return codeFromGrpcCode(grpcStatus.getCode());
}
private static StatusCanonicalCode codeFromGrpcCode(Code grpcCode) {
StatusCanonicalCode code = CODE_MAP.get(grpcCode);
return code != null ? code : StatusCanonicalCode.UNSET;
}
private GrpcHelper() {}
}

View File

@ -0,0 +1,37 @@
plugins {
id "java-library"
id "com.google.protobuf" version "0.8.13"
}
apply from: "$rootDir/gradle/java.gradle"
def grpcVersion = '1.5.0'
protobuf {
protoc {
// Download compiler rather than using locally installed version:
artifact = 'com.google.protobuf:protoc:3.3.0'
}
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}
dependencies {
api project(':testing-common')
api group: 'io.grpc', name: 'grpc-core', version: grpcVersion
api group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion
api group: 'io.grpc', name: 'grpc-stub', version: grpcVersion
implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
implementation deps.guava
implementation deps.groovy
implementation deps.opentelemetryApi
implementation deps.spock
}

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.grpc.v1_5
import static io.opentelemetry.trace.Span.Kind.CLIENT import static io.opentelemetry.trace.Span.Kind.CLIENT
import static io.opentelemetry.trace.Span.Kind.SERVER import static io.opentelemetry.trace.Span.Kind.SERVER
@ -14,16 +16,22 @@ import io.grpc.ManagedChannelBuilder
import io.grpc.Server import io.grpc.Server
import io.grpc.ServerBuilder import io.grpc.ServerBuilder
import io.grpc.stub.StreamObserver import io.grpc.stub.StreamObserver
import io.opentelemetry.auto.test.AgentTestRunner import io.opentelemetry.auto.test.InstrumentationSpecification
import io.opentelemetry.auto.test.utils.PortUtils import io.opentelemetry.auto.test.utils.PortUtils
import io.opentelemetry.trace.attributes.SemanticAttributes import io.opentelemetry.trace.attributes.SemanticAttributes
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import spock.lang.Unroll
class GrpcStreamingTest extends AgentTestRunner { @Unroll
abstract class AbstractGrpcStreamingTest extends InstrumentationSpecification {
def "test conversation #name"() { abstract ServerBuilder configureServer(ServerBuilder server)
abstract ManagedChannelBuilder configureClient(ManagedChannelBuilder client)
def "test conversation #paramName"() {
setup: setup:
def msgCount = serverMessageCount def msgCount = serverMessageCount
def serverReceived = new CopyOnWriteArrayList<>() def serverReceived = new CopyOnWriteArrayList<>()
@ -58,8 +66,8 @@ class GrpcStreamingTest extends AgentTestRunner {
} }
} }
def port = PortUtils.randomOpenPort() def port = PortUtils.randomOpenPort()
Server server = ServerBuilder.forPort(port).addService(greeter).build().start() Server server = configureServer(ServerBuilder.forPort(port).addService(greeter)).build().start()
ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forAddress("localhost", port) ManagedChannelBuilder channelBuilder = configureClient(ManagedChannelBuilder.forAddress("localhost", port))
// Depending on the version of gRPC usePlainText may or may not take an argument. // Depending on the version of gRPC usePlainText may or may not take an argument.
try { try {
@ -94,12 +102,6 @@ class GrpcStreamingTest extends AgentTestRunner {
observer.onCompleted() observer.onCompleted()
then: then:
error.get() == null
TEST_WRITER.waitForTraces(1)
error.get() == null
serverReceived == clientRange.collect { "call $it" }
clientReceived == serverRange.collect { clientRange.collect { "call $it" } }.flatten().sort()
assertTraces(1) { assertTraces(1) {
trace(0, 2) { trace(0, 2) {
span(0) { span(0) {
@ -111,8 +113,6 @@ class GrpcStreamingTest extends AgentTestRunner {
"${SemanticAttributes.RPC_SYSTEM.key()}" "grpc" "${SemanticAttributes.RPC_SYSTEM.key()}" "grpc"
"${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter" "${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter"
"${SemanticAttributes.RPC_METHOD.key()}" "Conversation" "${SemanticAttributes.RPC_METHOD.key()}" "Conversation"
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key()}" port
} }
(1..(clientMessageCount * serverMessageCount)).each { (1..(clientMessageCount * serverMessageCount)).each {
def messageId = it def messageId = it
@ -150,6 +150,9 @@ class GrpcStreamingTest extends AgentTestRunner {
} }
} }
} }
error.get() == null
serverReceived == clientRange.collect { "call $it" }
clientReceived == serverRange.collect { clientRange.collect { "call $it" } }.flatten().sort()
cleanup: cleanup:
channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS) channel?.shutdownNow()?.awaitTermination(10, TimeUnit.SECONDS)

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.grpc.v1_5
import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
import static io.opentelemetry.trace.Span.Kind.CLIENT import static io.opentelemetry.trace.Span.Kind.CLIENT
@ -18,15 +20,21 @@ import io.grpc.ServerBuilder
import io.grpc.Status import io.grpc.Status
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import io.grpc.stub.StreamObserver import io.grpc.stub.StreamObserver
import io.opentelemetry.auto.test.AgentTestRunner import io.opentelemetry.auto.test.InstrumentationSpecification
import io.opentelemetry.auto.test.utils.PortUtils import io.opentelemetry.auto.test.utils.PortUtils
import io.opentelemetry.instrumentation.auto.grpc.common.GrpcHelper import io.opentelemetry.trace.StatusCanonicalCode
import io.opentelemetry.trace.attributes.SemanticAttributes import io.opentelemetry.trace.attributes.SemanticAttributes
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import spock.lang.Unroll
class GrpcTest extends AgentTestRunner { @Unroll
abstract class AbstractGrpcTest extends InstrumentationSpecification {
def "test request-response"() { abstract ServerBuilder configureServer(ServerBuilder server)
abstract ManagedChannelBuilder configureClient(ManagedChannelBuilder client)
def "test request-response #paramName"() {
setup: setup:
BindableService greeter = new GreeterGrpc.GreeterImplBase() { BindableService greeter = new GreeterGrpc.GreeterImplBase() {
@Override @Override
@ -38,8 +46,8 @@ class GrpcTest extends AgentTestRunner {
} }
} }
def port = PortUtils.randomOpenPort() def port = PortUtils.randomOpenPort()
Server server = ServerBuilder.forPort(port).addService(greeter).build().start() Server server = configureServer(ServerBuilder.forPort(port).addService(greeter)).build().start()
ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forAddress("localhost", port) ManagedChannelBuilder channelBuilder = configureClient(ManagedChannelBuilder.forAddress("localhost", port))
// Depending on the version of gRPC usePlainText may or may not take an argument. // Depending on the version of gRPC usePlainText may or may not take an argument.
try { try {
@ -77,8 +85,6 @@ class GrpcTest extends AgentTestRunner {
"${SemanticAttributes.RPC_SYSTEM.key()}" "grpc" "${SemanticAttributes.RPC_SYSTEM.key()}" "grpc"
"${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter" "${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter"
"${SemanticAttributes.RPC_METHOD.key()}" "SayHello" "${SemanticAttributes.RPC_METHOD.key()}" "SayHello"
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key()}" port
} }
} }
span(2) { span(2) {
@ -112,7 +118,7 @@ class GrpcTest extends AgentTestRunner {
paramName << ["some name", "some other name"] paramName << ["some name", "some other name"]
} }
def "test error - #name"() { def "test error - #paramName"() {
setup: setup:
def error = grpcStatus.asException() def error = grpcStatus.asException()
BindableService greeter = new GreeterGrpc.GreeterImplBase() { BindableService greeter = new GreeterGrpc.GreeterImplBase() {
@ -123,8 +129,8 @@ class GrpcTest extends AgentTestRunner {
} }
} }
def port = PortUtils.randomOpenPort() def port = PortUtils.randomOpenPort()
Server server = ServerBuilder.forPort(port).addService(greeter).build().start() Server server = configureServer(ServerBuilder.forPort(port).addService(greeter)).build().start()
ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forAddress("localhost", port) ManagedChannelBuilder channelBuilder = configureClient(ManagedChannelBuilder.forAddress("localhost", port))
// Depending on the version of gRPC usePlainText may or may not take an argument. // Depending on the version of gRPC usePlainText may or may not take an argument.
try { try {
@ -148,13 +154,11 @@ class GrpcTest extends AgentTestRunner {
kind CLIENT kind CLIENT
hasNoParent() hasNoParent()
errored true errored true
status(GrpcHelper.statusFromGrpcStatus(grpcStatus)) status(StatusCanonicalCode.ERROR)
attributes { attributes {
"${SemanticAttributes.RPC_SYSTEM.key()}" "grpc" "${SemanticAttributes.RPC_SYSTEM.key()}" "grpc"
"${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter" "${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter"
"${SemanticAttributes.RPC_METHOD.key()}" "SayHello" "${SemanticAttributes.RPC_METHOD.key()}" "SayHello"
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key()}" port
} }
} }
span(1) { span(1) {
@ -162,7 +166,7 @@ class GrpcTest extends AgentTestRunner {
kind SERVER kind SERVER
childOf span(0) childOf span(0)
errored true errored true
status(GrpcHelper.statusFromGrpcStatus(grpcStatus)) status(StatusCanonicalCode.ERROR)
event(0) { event(0) {
eventName "message" eventName "message"
attributes { attributes {
@ -198,7 +202,7 @@ class GrpcTest extends AgentTestRunner {
"StatusRuntime - description" | Status.UNIMPLEMENTED.withDescription("some description") "StatusRuntime - description" | Status.UNIMPLEMENTED.withDescription("some description")
} }
def "test error thrown - #name"() { def "test error thrown - #paramName"() {
setup: setup:
def error = grpcStatus.asRuntimeException() def error = grpcStatus.asRuntimeException()
BindableService greeter = new GreeterGrpc.GreeterImplBase() { BindableService greeter = new GreeterGrpc.GreeterImplBase() {
@ -209,8 +213,8 @@ class GrpcTest extends AgentTestRunner {
} }
} }
def port = PortUtils.randomOpenPort() def port = PortUtils.randomOpenPort()
Server server = ServerBuilder.forPort(port).addService(greeter).build().start() Server server = configureServer(ServerBuilder.forPort(port).addService(greeter)).build().start()
ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forAddress("localhost", port) ManagedChannelBuilder channelBuilder = configureClient(ManagedChannelBuilder.forAddress("localhost", port))
// Depending on the version of gRPC usePlainText may or may not take an argument. // Depending on the version of gRPC usePlainText may or may not take an argument.
try { try {
@ -240,8 +244,6 @@ class GrpcTest extends AgentTestRunner {
"${SemanticAttributes.RPC_SYSTEM.key()}" "grpc" "${SemanticAttributes.RPC_SYSTEM.key()}" "grpc"
"${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter" "${SemanticAttributes.RPC_SERVICE.key()}" "example.Greeter"
"${SemanticAttributes.RPC_METHOD.key()}" "SayHello" "${SemanticAttributes.RPC_METHOD.key()}" "SayHello"
"${SemanticAttributes.NET_PEER_NAME.key()}" "localhost"
"${SemanticAttributes.NET_PEER_PORT.key()}" Long
} }
} }
span(1) { span(1) {
@ -249,7 +251,7 @@ class GrpcTest extends AgentTestRunner {
kind SERVER kind SERVER
childOf span(0) childOf span(0)
errored true errored true
status(GrpcHelper.statusFromGrpcStatus(grpcStatus)) status(StatusCanonicalCode.ERROR)
event(0) { event(0) {
eventName "message" eventName "message"
attributes { attributes {

View File

@ -88,7 +88,9 @@ include ':instrumentation:geode-1.4'
include ':instrumentation:google-http-client-1.19' include ':instrumentation:google-http-client-1.19'
include ':instrumentation:grizzly-2.0' include ':instrumentation:grizzly-2.0'
include ':instrumentation:grizzly-client-1.9' include ':instrumentation:grizzly-client-1.9'
include ':instrumentation:grpc-1.5' include ':instrumentation:grpc-1.5:auto'
include ':instrumentation:grpc-1.5:library'
include ':instrumentation:grpc-1.5:testing'
include ':instrumentation:guava-10.0' include ':instrumentation:guava-10.0'
include ':instrumentation:hibernate:hibernate-3.3' include ':instrumentation:hibernate:hibernate-3.3'
include ':instrumentation:hibernate:hibernate-4.0' include ':instrumentation:hibernate:hibernate-4.0'