Convert Rediscala to Instrumenter API (#4152)
This commit is contained in:
parent
07b5d667ec
commit
88ce6af1d6
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() {}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue