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;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaClientTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.rediscala.RediscalaSingletons.instrumenter;
import io.opentelemetry.context.Context;
import redis.RedisCommand;
import scala.runtime.AbstractFunction1;
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 RedisCommand<?, ?> request;
public OnCompleteHandler(Context context) {
public OnCompleteHandler(Context context, RedisCommand<?, ?> request) {
this.context = context;
this.request = request;
}
@Override
public Void apply(Try<Object> result) {
Throwable error = null;
if (result.isFailure()) {
tracer().endExceptionally(context, result.failed().get());
} else {
tracer().end(context);
error = result.failed().get();
}
instrumenter().end(context, request, null, error);
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.hasSuperType;
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.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
@ -62,23 +62,34 @@ public class RequestInstrumentation implements TypeInstrumentation {
@Advice.Argument(0) RedisCommand<?, ?> cmd,
@Advice.Local("otelContext") Context context,
@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();
}
@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("otelScope") Scope scope,
@Advice.Thrown Throwable throwable,
@Advice.FieldValue("executionContext") ExecutionContext ctx,
@Advice.Return(readOnly = false) Future<Object> responseFuture) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
instrumenter().end(context, cmd, null, throwable);
} 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) {
trace(0, 1) {
span(0) {
name "Set"
name "SET"
kind CLIENT
attributes {
"${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"() {
when:
def write = redisClient.set("bar",
"baz",
Option.apply(null),
Option.apply(null),
false,
false,
new ByteStringSerializerLowPriority.String$())
def value = redisClient.get("bar", new ByteStringDeserializerDefault.String$())
def (write, value) = runWithSpan("parent") {
def w = redisClient.set("bar",
"baz",
Option.apply(null),
Option.apply(null),
false,
false,
new ByteStringSerializerLowPriority.String$())
def v = redisClient.get("bar", new ByteStringDeserializerDefault.String$())
return new Tuple(w, v)
}
then:
Await.result(write, Duration.apply("3 second")) == true
Await.result(value, Duration.apply("3 second")) == Option.apply("baz")
assertTraces(2) {
trace(0, 1) {
assertTraces(1) {
trace(0, 3) {
span(0) {
name "Set"
name "parent"
}
span(1) {
name "SET"
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.DB_SYSTEM.key}" "redis"
"${SemanticAttributes.DB_STATEMENT.key}" "Set"
"${SemanticAttributes.DB_OPERATION.key}" "SET"
}
}
}
trace(1, 1) {
span(0) {
name "Get"
span(2) {
name "GET"
kind CLIENT
childOf span(0)
attributes {
"${SemanticAttributes.DB_SYSTEM.key}" "redis"
"${SemanticAttributes.DB_STATEMENT.key}" "Get"
"${SemanticAttributes.DB_OPERATION.key}" "GET"
}
}
}