Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Valkey instrumentation support #8228

Merged
merged 8 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions dd-java-agent/instrumentation/valkey-java/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

muzzle {
fail {
AhmadMasry marked this conversation as resolved.
Show resolved Hide resolved
group = "io.valkey"
module = "valkey-java"
versions = "[,5.3.0)"
}

pass {
group = "io.valkey"
module = "valkey-java"
versions = "[5.3.0,)"
}
}

apply from: "$rootDir/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
compileOnly group: 'io.valkey', name: 'valkey-java', version: '5.3.0'

testImplementation group: 'com.github.codemonstur', name: 'embedded-redis', version: '1.4.3'
testImplementation group: 'io.valkey', name: 'valkey-java', version: '5.3.0'
latestDepTestImplementation group: 'io.valkey', name: 'valkey-java', version: '5.+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package datadog.trace.instrumentation.valkey;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static io.valkey.ValkeyClientDecorator.DECORATE;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import io.valkey.CommandObject;
import io.valkey.Connection;
import io.valkey.Protocol;
import io.valkey.ValkeyClientDecorator;
import io.valkey.commands.ProtocolCommand;
import net.bytebuddy.asm.Advice;

@AutoService(InstrumenterModule.class)
public final class ValkeyInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {

public ValkeyInstrumentation() {
super("valkey");
}

@Override
public String instrumentedType() {
return "io.valkey.Connection";
}

@Override
public String[] helperClassNames() {
return new String[] {
"io.valkey.ValkeyClientDecorator",
};
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod()
.and(isPublic())
.and(named("executeCommand"))
.and(takesArgument(0, named("io.valkey.CommandObject"))),
ValkeyInstrumentation.class.getName() + "$ValkeyAdvice");
}

public static class ValkeyAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope onEnter(
@Advice.Argument(0) final CommandObject<?> commandObject,
@Advice.This final Connection thiz) {
final AgentSpan span = startSpan("valkey", ValkeyClientDecorator.OPERATION_NAME);
DECORATE.afterStart(span);
DECORATE.onConnection(span, thiz);

final ProtocolCommand command = commandObject.getArguments().getCommand();

if (command instanceof Protocol.Command) {
AhmadMasry marked this conversation as resolved.
Show resolved Hide resolved
DECORATE.onStatement(span, ((Protocol.Command) command).name());
} else {
DECORATE.onStatement(span, new String(command.getRaw()));
}
return activateSpan(span);
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
DECORATE.onError(scope.span(), throwable);
DECORATE.beforeFinish(scope.span());
scope.close();
scope.span().finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.valkey;

import datadog.trace.api.naming.SpanNaming;
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.DBTypeProcessingDatabaseClientDecorator;

public class ValkeyClientDecorator extends DBTypeProcessingDatabaseClientDecorator<Connection> {
public static final ValkeyClientDecorator DECORATE = new ValkeyClientDecorator();

private static final String VALKEY = "valkey";
public static final CharSequence OPERATION_NAME =
UTF8BytesString.create(SpanNaming.instance().namingSchema().cache().operation(VALKEY));
private static final String SERVICE_NAME =
SpanNaming.instance().namingSchema().cache().service(VALKEY);
private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("valkey-command");

@Override
protected String[] instrumentationNames() {
return new String[] {"valkey", VALKEY};
}

@Override
protected String service() {
return SERVICE_NAME;
}

@Override
protected CharSequence component() {
return COMPONENT_NAME;
}

@Override
protected CharSequence spanType() {
return InternalSpanTypes.VALKEY;
}

@Override
protected String dbType() {
return VALKEY;
}

@Override
protected String dbUser(final Connection connection) {
return null;
}

@Override
protected String dbInstance(final Connection connection) {
return null;
}

@Override
protected String dbHostname(Connection connection) {
return connection.getHostAndPort().getHost();
}
}
Loading