diff --git a/instrumentation/jedis-3.0.0/build.gradle b/instrumentation/jedis-3.0.0/build.gradle index 9a04e772c9..4860b8610b 100644 --- a/instrumentation/jedis-3.0.0/build.gradle +++ b/instrumentation/jedis-3.0.0/build.gradle @@ -13,7 +13,7 @@ dependencies { verifyInstrumentation { 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,)' +// 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' diff --git a/instrumentation/jedis-4.0.0/build.gradle b/instrumentation/jedis-4.0.0/build.gradle new file mode 100644 index 0000000000..fa936d9b45 --- /dev/null +++ b/instrumentation/jedis-4.0.0/build.gradle @@ -0,0 +1,24 @@ +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]' + excludeRegex 'redis.clients:jedis:.*-(m|rc|RC|beta)[0-9]*' + exclude 'redis.clients:jedis:3.6.2' + +} + +site { + title 'Jedis' + type 'Datastore' +} \ No newline at end of file diff --git a/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/Connection_Instrumentation.java b/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/Connection_Instrumentation.java new file mode 100644 index 0000000000..cd4135685e --- /dev/null +++ b/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/Connection_Instrumentation.java @@ -0,0 +1,91 @@ +/* + * + * * 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.commands.ProtocolCommand; + +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; + + abstract HostAndPort getHostAndPort(); + + public void disconnect() { + db = 0; + Weaver.callOriginal(); + } + + @Trace + public void sendCommand(final ProtocolCommand cmd, final byte[]... args) { + Weaver.callOriginal(); + + updateDbIndex(cmd, new String(args[0], StandardCharsets.UTF_8)); + + } + + @Trace + public void sendCommand(final ProtocolCommand cmd, final String... args) { + Weaver.callOriginal(); + + updateDbIndex(cmd, args[0]); + } + + @Trace(leaf = true) + public void sendCommand(final CommandArguments args) { + Weaver.callOriginal(); + + ProtocolCommand cmd = args.getCommand(); + reportMethodAsExternal(cmd); + + } + + 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 operation = "unknown"; + try { + + 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(getHostAndPort().getHost(), getHostAndPort().getPort()) + .databaseName(String.valueOf(db)) + .build()); + } + +} diff --git a/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/JedisPubSub_Instrumentation.java b/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/JedisPubSub_Instrumentation.java new file mode 100644 index 0000000000..fcc2a88818 --- /dev/null +++ b/instrumentation/jedis-4.0.0/src/main/java/redis/clients/jedis/JedisPubSub_Instrumentation.java @@ -0,0 +1,83 @@ +/* + * + * * 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.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@SuppressWarnings({ "ResultOfMethodCallIgnored", "unused" }) +@Weave(type = MatchType.ExactClass, originalName = "redis.clients.jedis.JedisPubSub") +public class JedisPubSub_Instrumentation { + + private volatile Connection client; + + public void proceed(Connection client, String... channels) { + Weaver.callOriginal(); + } + + public void proceedWithPatterns(Connection client, String... channels) { + Weaver.callOriginal(); + } + + @Trace + public void onMessage(String channel, String message) { + Weaver.callOriginal(); + + reportMethodAsExternal("message"); + } + + @Trace + public void onPMessage(String pattern, String channel, String message) { + Weaver.callOriginal(); + + reportMethodAsExternal("message"); + } + + @Trace + public void onSubscribe(String channel, int subscribedChannels) { + Weaver.callOriginal(); + + reportMethodAsExternal("subscribe"); + } + + @Trace + public void onUnsubscribe(String channel, int subscribedChannels) { + Weaver.callOriginal(); + + reportMethodAsExternal("unsubscribe"); + } + + @Trace + public void onPUnsubscribe(String pattern, int subscribedChannels) { + Weaver.callOriginal(); + + reportMethodAsExternal("unsubscribe"); + } + + @Trace + public void onPSubscribe(String pattern, int subscribedChannels) { + Weaver.callOriginal(); + + reportMethodAsExternal("subscribe"); + } + + private void reportMethodAsExternal(String commandName) { + NewRelic.getAgent().getTracedMethod().reportAsExternal(DatastoreParameters + .product(DatastoreVendor.Redis.name()) + .collection(null) + .operation(commandName) + .instance(client.getHostAndPort().getHost(), client.getHostAndPort().getPort()) + .build()); + + } +} diff --git a/settings.gradle b/settings.gradle index 05e1f03508..8f82e06dc1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -170,6 +170,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'