Skip to content

Commit

Permalink
jedis 4.0.0+ instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tbradellis committed Feb 15, 2022
1 parent f1897ca commit df5d9b8
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 3 deletions.
4 changes: 1 addition & 3 deletions instrumentation/jedis-3.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
apply plugin: 'gradle-verify-instrumentation-plugin'

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.jedis-3.0.0' }
}
Expand All @@ -13,7 +11,7 @@ dependencies {
}

verifyInstrumentation {
passes 'redis.clients:jedis:[3.0.0,4.0.0]'
passes 'redis.clients:jedis:[3.0.0,4.0.0)'
fails 'redis.clients:jedis:[1.4.0,3.0.0)'
// fails 'redis.clients:jedis:[4.0.0,)'
excludeRegex 'redis.clients:jedis:.*-(m|rc|RC|beta)[0-9]*'
Expand Down
25 changes: 25 additions & 0 deletions instrumentation/jedis-4.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.jedis-4.0.0' }
}

dependencies {
implementation(project(":agent-bridge"))
implementation(project(":agent-bridge-datastore"))
implementation(project(":newrelic-api"))
implementation(project(":newrelic-weaver-api"))
implementation("redis.clients:jedis:4.0.0")
}

verifyInstrumentation {
passes 'redis.clients:jedis:[4.0.0,)'
fails 'redis.clients:jedis:[1.4.0,3.8.0]'
// fails 'redis.clients:jedis:[4.0.0,)'
excludeRegex 'redis.clients:jedis:.*-(m|rc|RC|beta)[0-9]*'
// there's something weird about how the verifier treats this version
// exclude 'redis.clients:jedis:3.6.2'
}

site {
title 'Jedis'
type 'Datastore'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package redis.clients.jedis;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.datastore.DatastoreVendor;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import redis.clients.jedis.args.Rawable;
import redis.clients.jedis.commands.ProtocolCommand;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;

@SuppressWarnings({ "ResultOfMethodCallIgnored", "WeakerAccess", "unused" }) // Weaver.callOriginal(), matching signatures
@Weave(type = MatchType.BaseClass, originalName = "redis.clients.jedis.Connection")
public abstract class Connection_Instrumentation {

@NewField
private long db = 0;

@NewField
private String host;

@NewField
private int port;

@NewField
private HostAndPort hostAndPort;

final HostAndPort getHostAndPort(){
this.hostAndPort = Weaver.callOriginal();
this.host = this.hostAndPort.getHost();
this.port = this.hostAndPort.getPort();
return this.hostAndPort;

}

public void disconnect() {
db = 0;
Weaver.callOriginal();
}

@Trace(leaf = true)
public void sendCommand(final ProtocolCommand cmd, final byte[]... args) {
Weaver.callOriginal();

updateDbIndex(cmd, new String(args[0], StandardCharsets.UTF_8));
reportMethodAsExternal(cmd, this.host, this.port);
}

@Trace(leaf = true)
public void sendCommand(final ProtocolCommand cmd, final String... args) {
Weaver.callOriginal();

updateDbIndex(cmd, args[0]);
reportMethodAsExternal(cmd, this.host, this.port);
}

@Trace(leaf = true)
public void sendCommand(final ProtocolCommand cmd) {
Weaver.callOriginal();

reportMethodAsExternal(cmd, this.host, this.port);
}

@Trace(leaf = true)
public void sendCommand(final CommandArguments args){
Weaver.callOriginal();

reportMethodAsExternal(args.getCommand(), this.host, this.port);

}

private void updateDbIndex(ProtocolCommand cmd, String arg0) {
try {
if (cmd == Protocol.Command.SELECT) {
db = Long.parseLong(arg0);
} else if (cmd == Protocol.Command.QUIT) {
db = 0;
}
} catch (Throwable t) {
AgentBridge.getAgent().getLogger().log(Level.FINER, t, "Unable to set DB index");
}
}

private void reportMethodAsExternal(ProtocolCommand command, String serverUsed, int serverPortUsed) {
String operation = "unknown";
try {
//TODO: investigate eliminating new String usage. Was it used here for a good reason?
//Seems unlikely there's a good reason and all of these usages should benefit from the String pool since
//the commands are from a known bound set of Strings. Maybe the bytes need the charset defined? default for jdk should be UTF-8 anyway.
operation = new String(command.getRaw(), Protocol.CHARSET).toLowerCase();
} catch (Exception ignored) { }

NewRelic.getAgent().getTracedMethod().reportAsExternal(DatastoreParameters
.product(DatastoreVendor.Redis.name())
.collection(null)
.operation(operation)
.instance(serverUsed, serverPortUsed)
.databaseName(String.valueOf(db))
.build());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package redis.clients.jedis;

import com.newrelic.agent.bridge.datastore.DatastoreVendor;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

@SuppressWarnings({ "ResultOfMethodCallIgnored", "unused" })
@Weave(type = MatchType.BaseClass, originalName = "redis.clients.jedis.JedisPubSub")
public class JedisPubSub_Instrumentation {

@NewField
private String host;

@NewField
private int port;

@Trace
private void process() {
Weaver.callOriginal();
}

public void proceed(Connection client, String... channels){
this.host = client.getHostAndPort().getHost();
this.port = client.getHostAndPort().getPort();
Weaver.callOriginal();
}

@Trace
public void onMessage(String channel, String message) {
Weaver.callOriginal();

reportMethodAsExternal("message", host, port);
}

@Trace
public void onPMessage(String pattern, String channel, String message) {
Weaver.callOriginal();

reportMethodAsExternal("message", host, port);
}

@Trace
public void onSubscribe(String channel, int subscribedChannels) {
Weaver.callOriginal();

reportMethodAsExternal("subscribe", host, port);
}

@Trace
public void onUnsubscribe(String channel, int subscribedChannels) {
Weaver.callOriginal();

reportMethodAsExternal("unsubscribe", host, port);
}

@Trace
public void onPUnsubscribe(String pattern, int subscribedChannels) {
Weaver.callOriginal();

reportMethodAsExternal("unsubscribe", host, port);
}

@Trace
public void onPSubscribe(String pattern, int subscribedChannels) {
Weaver.callOriginal();

reportMethodAsExternal("subscribe", host, port);
}

private void reportMethodAsExternal(String commandName, String host, int port) {
NewRelic.getAgent().getTracedMethod().reportAsExternal(DatastoreParameters
.product(DatastoreVendor.Redis.name())
.collection(null)
.operation(commandName)
.instance(host, port)
.build());
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ include 'instrumentation:jedis-1.4.0'
include 'instrumentation:jedis-2.7.1'
include 'instrumentation:jedis-2.7.2'
include 'instrumentation:jedis-3.0.0'
include 'instrumentation:jedis-4.0.0'
include 'instrumentation:jersey-1'
include 'instrumentation:jersey-2'
include 'instrumentation:jersey-client-1'
Expand Down

0 comments on commit df5d9b8

Please sign in to comment.