Convert Rediscala to Instrumenter API (#4152)

This commit is contained in:
Mateusz Rzeszutek 2021-09-17 19:22:58 +02:00 committed by GitHub
parent 07b5d667ec
commit 88ce6af1d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 89 deletions

View File

@ -5,26 +5,29 @@
package io.opentelemetry.javaagent.instrumentation.rediscala; package io.opentelemetry.javaagent.instrumentation.rediscala;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaSingletons.instrumenter;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import redis.RedisCommand;
import scala.runtime.AbstractFunction1; import scala.runtime.AbstractFunction1;
import scala.util.Try; import scala.util.Try;
public class OnCompleteHandler extends AbstractFunction1<Try<Object>, Void> { public final class OnCompleteHandler extends AbstractFunction1<Try<Object>, Void> {
private final Context context; private final Context context;
private final RedisCommand<?, ?> request;
public OnCompleteHandler(Context context) { public OnCompleteHandler(Context context, RedisCommand<?, ?> request) {
this.context = context; this.context = context;
this.request = request;
} }
@Override @Override
public Void apply(Try<Object> result) { public Void apply(Try<Object> result) {
Throwable error = null;
if (result.isFailure()) { if (result.isFailure()) {
tracer().endExceptionally(context, result.failed().get()); error = result.failed().get();
} else {
tracer().end(context);
} }
instrumenter().end(context, request, null, error);
return null; return null;
} }
} }

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.rediscala;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbAttributesExtractor;
import io.opentelemetry.instrumentation.api.tracer.ClassNames;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.Nullable;
import redis.RedisCommand;
final class RediscalaAttributesExtractor extends DbAttributesExtractor<RedisCommand<?, ?>, Void> {
@Override
protected String system(RedisCommand<?, ?> redisCommand) {
return SemanticAttributes.DbSystemValues.REDIS;
}
@Override
protected @Nullable String user(RedisCommand<?, ?> redisCommand) {
return null;
}
@Override
protected @Nullable String name(RedisCommand<?, ?> redisCommand) {
return null;
}
@Override
protected @Nullable String connectionString(RedisCommand<?, ?> redisCommand) {
return null;
}
@Override
protected @Nullable String statement(RedisCommand<?, ?> redisCommand) {
return null;
}
@Override
protected String operation(RedisCommand<?, ?> redisCommand) {
return ClassNames.simpleName(redisCommand.getClass()).toUpperCase(Locale.ROOT);
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.rediscala;
import io.opentelemetry.instrumentation.api.tracer.ClassNames;
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DbSystemValues;
import java.net.InetSocketAddress;
import redis.RedisCommand;
public class RediscalaClientTracer
extends DatabaseClientTracer<RedisCommand<?, ?>, RedisCommand<?, ?>, String> {
private static final RediscalaClientTracer TRACER = new RediscalaClientTracer();
private RediscalaClientTracer() {
super(NetPeerAttributes.INSTANCE);
}
public static RediscalaClientTracer tracer() {
return TRACER;
}
@Override
protected String sanitizeStatement(RedisCommand<?, ?> redisCommand) {
return ClassNames.simpleName(redisCommand.getClass());
}
@Override
protected String spanName(
RedisCommand<?, ?> connection, RedisCommand<?, ?> statement, String operation) {
return operation;
}
@Override
protected String dbSystem(RedisCommand<?, ?> redisCommand) {
return DbSystemValues.REDIS;
}
@Override
protected InetSocketAddress peerAddress(RedisCommand<?, ?> redisCommand) {
return null;
}
@Override
protected String dbStatement(
RedisCommand<?, ?> connection, RedisCommand<?, ?> command, String operation) {
return operation;
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.rediscala-1.8";
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.rediscala;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbSpanNameExtractor;
import redis.RedisCommand;
public final class RediscalaSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.rediscala-1.8";
private static final Instrumenter<RedisCommand<?, ?>, Void> INSTRUMENTER;
static {
RediscalaAttributesExtractor attributesExtractor = new RediscalaAttributesExtractor();
SpanNameExtractor<RedisCommand<?, ?>> spanNameExtractor =
DbSpanNameExtractor.create(attributesExtractor);
INSTRUMENTER =
Instrumenter.<RedisCommand<?, ?>, Void>newBuilder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
.addAttributesExtractor(attributesExtractor)
.newInstrumenter(SpanKindExtractor.alwaysClient());
}
public static Instrumenter<RedisCommand<?, ?>, Void> instrumenter() {
return INSTRUMENTER;
}
private RediscalaSingletons() {}
}

View File

@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.rediscala;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType;
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer; import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -62,23 +62,34 @@ public class RequestInstrumentation implements TypeInstrumentation {
@Advice.Argument(0) RedisCommand<?, ?> cmd, @Advice.Argument(0) RedisCommand<?, ?> cmd,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan(currentContext(), cmd, cmd);
Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, cmd)) {
return;
}
context = instrumenter().start(parentContext, cmd);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void onExit(
@Advice.Argument(0) RedisCommand<?, ?> cmd,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.FieldValue("executionContext") ExecutionContext ctx, @Advice.FieldValue("executionContext") ExecutionContext ctx,
@Advice.Return(readOnly = false) Future<Object> responseFuture) { @Advice.Return(readOnly = false) Future<Object> responseFuture) {
if (scope == null) {
return;
}
scope.close(); scope.close();
if (throwable != null) { if (throwable != null) {
tracer().endExceptionally(context, throwable); instrumenter().end(context, cmd, null, throwable);
} else { } else {
responseFuture.onComplete(new OnCompleteHandler(context), ctx); responseFuture.onComplete(new OnCompleteHandler(context, cmd), ctx);
} }
} }
} }

View File

@ -66,11 +66,11 @@ class RediscalaClientTest extends AgentInstrumentationSpecification {
assertTraces(1) { assertTraces(1) {
trace(0, 1) { trace(0, 1) {
span(0) { span(0) {
name "Set" name "SET"
kind CLIENT kind CLIENT
attributes { attributes {
"${SemanticAttributes.DB_SYSTEM.key}" "redis" "${SemanticAttributes.DB_SYSTEM.key}" "redis"
"${SemanticAttributes.DB_STATEMENT.key}" "Set" "${SemanticAttributes.DB_OPERATION.key}" "SET"
} }
} }
} }
@ -79,36 +79,42 @@ class RediscalaClientTest extends AgentInstrumentationSpecification {
def "get command"() { def "get command"() {
when: when:
def write = redisClient.set("bar", def (write, value) = runWithSpan("parent") {
"baz", def w = redisClient.set("bar",
Option.apply(null), "baz",
Option.apply(null), Option.apply(null),
false, Option.apply(null),
false, false,
new ByteStringSerializerLowPriority.String$()) false,
def value = redisClient.get("bar", new ByteStringDeserializerDefault.String$()) new ByteStringSerializerLowPriority.String$())
def v = redisClient.get("bar", new ByteStringDeserializerDefault.String$())
return new Tuple(w, v)
}
then: then:
Await.result(write, Duration.apply("3 second")) == true Await.result(write, Duration.apply("3 second")) == true
Await.result(value, Duration.apply("3 second")) == Option.apply("baz") Await.result(value, Duration.apply("3 second")) == Option.apply("baz")
assertTraces(2) { assertTraces(1) {
trace(0, 1) { trace(0, 3) {
span(0) { span(0) {
name "Set" name "parent"
}
span(1) {
name "SET"
kind CLIENT kind CLIENT
childOf span(0)
attributes { attributes {
"${SemanticAttributes.DB_SYSTEM.key}" "redis" "${SemanticAttributes.DB_SYSTEM.key}" "redis"
"${SemanticAttributes.DB_STATEMENT.key}" "Set" "${SemanticAttributes.DB_OPERATION.key}" "SET"
} }
} }
} span(2) {
trace(1, 1) { name "GET"
span(0) {
name "Get"
kind CLIENT kind CLIENT
childOf span(0)
attributes { attributes {
"${SemanticAttributes.DB_SYSTEM.key}" "redis" "${SemanticAttributes.DB_SYSTEM.key}" "redis"
"${SemanticAttributes.DB_STATEMENT.key}" "Get" "${SemanticAttributes.DB_OPERATION.key}" "GET"
} }
} }
} }