You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2023/01/16 03:46:44 UTC

[skywalking-java] branch main updated: Enhance lettuce plugin to adopt uniform tags (#441)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-java.git


The following commit(s) were added to refs/heads/main by this push:
     new f366503d58 Enhance lettuce plugin to adopt uniform tags (#441)
f366503d58 is described below

commit f366503d58aa7e3395d88f6c70e6ea885fc50d2e
Author: xzyJavaX <49...@users.noreply.github.com>
AuthorDate: Mon Jan 16 11:46:39 2023 +0800

    Enhance lettuce plugin to adopt uniform tags (#441)
---
 CHANGES.md                                         |   2 +-
 .../apm/plugin/lettuce/v5/LettucePluginConfig.java | 116 +++++++++++++++++++++
 .../lettuce/v5/RedisChannelWriterInterceptor.java  |  56 ++++++----
 .../v5/RedisChannelWriterInterceptorTest.java      |   3 +-
 apm-sniffer/config/agent.config                    |   4 +
 .../service-agent/java-agent/configurations.md     |   2 +
 .../lettuce-scenario/config/expectedData.yaml      |  18 ++--
 7 files changed, 172 insertions(+), 29 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 1bd7aa0d76..38c8a288a0 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,7 +5,7 @@ Release Notes.
 8.15.0
 ------------------
 
-
+* Enhance lettuce plugin to adopt uniform tags.
 
 #### Documentation
 
diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java
index 57cd760a85..4f6668d32e 100644
--- a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java
+++ b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java
@@ -20,6 +20,10 @@ package org.apache.skywalking.apm.plugin.lettuce.v5;
 
 import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 public class LettucePluginConfig {
     public static class Plugin {
         @PluginConfig(root = LettucePluginConfig.class)
@@ -36,6 +40,118 @@ public class LettucePluginConfig {
              * Set a negative number to save specified length of parameter string to the tag.
              */
             public static int REDIS_PARAMETER_MAX_LENGTH = 128;
+
+            /**
+             * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" usually
+             * This config term define which command should be converted to write Operation .
+             *
+             * @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP
+             * @see RedisChannelWriterInterceptor#parseOperation(String)
+             */
+            public static Set<String> OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList(
+                    "getset",
+                    "set",
+                    "setbit",
+                    "setex ",
+                    "setnx ",
+                    "setrange",
+                    "strlen ",
+                    "mset",
+                    "msetnx ",
+                    "psetex",
+                    "incr ",
+                    "incrby ",
+                    "incrbyfloat",
+                    "decr ",
+                    "decrby ",
+                    "append ",
+                    "hmset",
+                    "hset",
+                    "hsetnx ",
+                    "hincrby",
+                    "hincrbyfloat",
+                    "hdel",
+                    "rpoplpush",
+                    "rpush",
+                    "rpushx",
+                    "lpush",
+                    "lpushx",
+                    "lrem",
+                    "ltrim",
+                    "lset",
+                    "brpoplpush",
+                    "linsert",
+                    "sadd",
+                    "sdiff",
+                    "sdiffstore",
+                    "sinterstore",
+                    "sismember",
+                    "srem",
+                    "sunion",
+                    "sunionstore",
+                    "sinter",
+                    "zadd",
+                    "zincrby",
+                    "zinterstore",
+                    "zrange",
+                    "zrangebylex",
+                    "zrangebyscore",
+                    "zrank",
+                    "zrem",
+                    "zremrangebylex",
+                    "zremrangebyrank",
+                    "zremrangebyscore",
+                    "zrevrange",
+                    "zrevrangebyscore",
+                    "zrevrank",
+                    "zunionstore",
+                    "xadd",
+                    "xdel",
+                    "del",
+                    "xtrim"
+            ));
+            /**
+             * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" usually
+             * This config term define which command should be converted to write Operation .
+             *
+             * @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP
+             * @see RedisChannelWriterInterceptor#parseOperation(String)
+             */
+            public static Set<String> OPERATION_MAPPING_READ = new HashSet<>(Arrays.asList(
+                    "getrange",
+                    "getbit ",
+                    "mget",
+                    "hvals",
+                    "hkeys",
+                    "hlen",
+                    "hexists",
+                    "hget",
+                    "hgetall",
+                    "hmget",
+                    "blpop",
+                    "brpop",
+                    "lindex",
+                    "llen",
+                    "lpop",
+                    "lrange",
+                    "rpop",
+                    "scard",
+                    "srandmember",
+                    "spop",
+                    "sscan",
+                    "smove",
+                    "zlexcount",
+                    "zscore",
+                    "zscan",
+                    "zcard",
+                    "zcount",
+                    "xget",
+                    "get",
+                    "xread",
+                    "xlen",
+                    "xrange",
+                    "xrevrange"
+            ));
         }
     }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptor.java b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptor.java
index 8d780d2bcb..3e5c670e77 100644
--- a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptor.java
@@ -18,6 +18,7 @@
 
 package org.apache.skywalking.apm.plugin.lettuce.v5;
 
+import io.lettuce.core.codec.StringCodec;
 import io.lettuce.core.protocol.CommandArgs;
 import io.lettuce.core.protocol.DecoratedCommand;
 import io.lettuce.core.protocol.RedisCommand;
@@ -33,16 +34,18 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
 import org.apache.skywalking.apm.util.StringUtil;
 
 import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 
 @SuppressWarnings("unchecked")
 public class RedisChannelWriterInterceptor implements InstanceMethodsAroundInterceptor {
 
     private static final String PASSWORD_MASK = "******";
     private static final String ABBR = "...";
-    private static final String DELIMITER_SPACE = " ";
     private static final String AUTH = "AUTH";
+    private static final StringCodec STRING_CODEC = new StringCodec();
 
     @Override
     public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) {
@@ -63,45 +66,48 @@ public class RedisChannelWriterInterceptor implements InstanceMethodsAroundInter
             return;
         }
 
-        StringBuilder dbStatement = new StringBuilder();
         String operationName = "Lettuce/";
+        String key = Constants.EMPTY_STRING;
+        String command = Constants.EMPTY_STRING;
         if (allArguments[0] instanceof RedisCommand) {
             RedisCommand<?, ?, ?> redisCommand = (RedisCommand<?, ?, ?>) allArguments[0];
-            String command = redisCommand.getType().name();
+            command = redisCommand.getType().name();
             operationName = operationName + command;
-            dbStatement.append(command);
             if (LettucePluginConfig.Plugin.Lettuce.TRACE_REDIS_PARAMETERS) {
-                dbStatement.append(DELIMITER_SPACE).append(getArgsStatement(redisCommand));
+                key = getArgsKey(redisCommand);
             }
         } else if (allArguments[0] instanceof Collection) {
-            Collection<RedisCommand<?, ?, ?>> redisCommands = (Collection<RedisCommand<?, ?, ?>>) allArguments[0];
             operationName = operationName + "BATCH_WRITE";
-            for (RedisCommand<?, ?, ?> redisCommand : redisCommands) {
-                dbStatement.append(redisCommand.getType().name()).append(";");
-            }
+            command = "BATCH_WRITE";
         }
         AbstractSpan span = ContextManager.createExitSpan(operationName, peer);
         span.setComponent(ComponentsDefine.LETTUCE);
-        Tags.DB_TYPE.set(span, "Redis");
-        Tags.DB_STATEMENT.set(span, dbStatement.toString());
+        Tags.CACHE_TYPE.set(span, "Redis");
+        if (StringUtil.isNotEmpty(key)) {
+            Tags.CACHE_KEY.set(span, key);
+        }
+        Tags.CACHE_CMD.set(span, command);
+        parseOperation(command.toLowerCase()).ifPresent(op -> Tags.CACHE_OP.set(span, op));
         SpanLayer.asCache(span);
         span.prepareForAsync();
         ContextManager.stopSpan();
         enhancedCommand.setSkyWalkingDynamicField(span);
     }
 
-    private String getArgsStatement(RedisCommand<?, ?, ?> redisCommand) {
-        String statement;
+    private String getArgsKey(RedisCommand<?, ?, ?> redisCommand) {
         if (AUTH.equalsIgnoreCase(redisCommand.getType().name())) {
-            statement = PASSWORD_MASK;
-        } else {
-            CommandArgs<?, ?> args = redisCommand.getArgs();
-            statement = (args != null) ? args.toCommandString() : Constants.EMPTY_STRING;
+            return PASSWORD_MASK;
         }
-        if (StringUtil.isNotEmpty(statement) && statement.length() > LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) {
-            statement = statement.substring(0, LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) + ABBR;
+        CommandArgs<?, ?> args = redisCommand.getArgs();
+        if (args == null) {
+            return Constants.EMPTY_STRING;
+        } 
+        ByteBuffer firstEncodedKey = args.getFirstEncodedKey();
+        String key = STRING_CODEC.decodeKey(firstEncodedKey);    
+        if (StringUtil.isNotEmpty(key) && key.length() > LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) {
+            key = StringUtil.cut(key, LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) + ABBR;
         }
-        return statement;
+        return key;
     }
 
     @Override
@@ -144,4 +150,14 @@ public class RedisChannelWriterInterceptor implements InstanceMethodsAroundInter
         }
         return command;
     }
+
+    private Optional<String> parseOperation(String cmd) {
+        if (LettucePluginConfig.Plugin.Lettuce.OPERATION_MAPPING_READ.contains(cmd)) {
+            return Optional.of("read");
+        }
+        if (LettucePluginConfig.Plugin.Lettuce.OPERATION_MAPPING_WRITE.contains(cmd)) {
+            return Optional.of("write");
+        }
+        return Optional.empty();
+    }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorTest.java
index 95d50afec8..72cd0b3d59 100644
--- a/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorTest.java
+++ b/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorTest.java
@@ -121,7 +121,7 @@ public class RedisChannelWriterInterceptorTest {
 
     @Test
     public void testInterceptor() {
-        CommandArgs<?, ?> args = new CommandArgs<>(new ByteArrayCodec()).addKey("name".getBytes()).addValue("Tom".getBytes());
+        CommandArgs<?, ?> args = new CommandArgs<>(new ByteArrayCodec()).addKey("name".getBytes());
         MockRedisCommand<?, ?, ?> redisCommand = new MockRedisCommand<>(CommandType.SET, null, args);
         interceptor.beforeMethod(mockRedisChannelWriterInstance, null, new Object[]{redisCommand}, null, null);
         interceptor.afterMethod(mockRedisChannelWriterInstance, null, null, null, null);
@@ -136,7 +136,6 @@ public class RedisChannelWriterInterceptorTest {
         assertThat(SpanHelper.getComponentId(spans.get(0)), is(57));
         List<TagValuePair> tags = SpanHelper.getTags(spans.get(0));
         assertThat(tags.get(0).getValue(), is("Redis"));
-        assertThat(tags.get(1).getValue(), CoreMatchers.containsString("Tom"));
         assertThat(SpanHelper.getLayer(spans.get(0)), CoreMatchers.is(SpanLayer.CACHE));
         assertThat(SpanHelper.getPeer(spans.get(0)), is(PEER));
     }
diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config
index 2e7bc49160..a3a335f7b6 100755
--- a/apm-sniffer/config/agent.config
+++ b/apm-sniffer/config/agent.config
@@ -269,6 +269,10 @@ plugin.toolkit.log.transmit_formatted=${SW_PLUGIN_TOOLKIT_LOG_TRANSMIT_FORMATTED
 plugin.lettuce.trace_redis_parameters=${SW_PLUGIN_LETTUCE_TRACE_REDIS_PARAMETERS:false}
 #  If set to positive number and `plugin.lettuce.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length.
 plugin.lettuce.redis_parameter_max_length=${SW_PLUGIN_LETTUCE_REDIS_PARAMETER_MAX_LENGTH:128}
+# Specify which command should be converted to write operation
+plugin.lettuce.operation_mapping_write=${SW_PLUGIN_LETTUCE_OPERATION_MAPPING_WRITE:getset,set,setbit,setex,setnx,setrange,strlen,mset,msetnx,psetex,incr,incrby,incrbyfloat,decr,decrby,append,hmset,hset,hsetnx,hincrby,hincrbyfloat,hdel,rpoplpush,rpush,rpushx,lpush,lpushx,lrem,ltrim,lset,brpoplpush,linsert,sadd,sdiff,sdiffstore,sinterstore,sismember,srem,sunion,sunionstore,sinter,zadd,zincrby,zinterstore,zrange,zrangebylex,zrangebyscore,zrank,zrem,zremrangebylex,zremrangebyrank,zremrangeby [...]
+# Specify which command should be converted to read operation
+plugin.lettuce.operation_mapping_read=${SW_PLUGIN_LETTUCE_OPERATION_MAPPING_READ:getrange,getbit,mget,hvals,hkeys,hlen,hexists,hget,hgetall,hmget,blpop,brpop,lindex,llen,lpop,lrange,rpop,scard,srandmember,spop,sscan,smove,zlexcount,zscore,zscan,zcard,zcount,xget,get,xread,xlen,xrange,xrevrange}
 # If set to true, the parameters of the cypher would be collected.
 plugin.neo4j.trace_cypher_parameters=${SW_PLUGIN_NEO4J_TRACE_CYPHER_PARAMETERS:false}
 # If set to positive number, the `db.cypher.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.
diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md
index 9669e9398f..8aafa53908 100644
--- a/docs/en/setup/service-agent/java-agent/configurations.md
+++ b/docs/en/setup/service-agent/java-agent/configurations.md
@@ -106,6 +106,8 @@ This is the properties list supported in `agent/config/agent.config`.
 | `plugin.toolkit.log.transmit_formatted`                         | Whether or not to transmit logged data as formatted or un-formatted.                                                                                                                                                                                                                                                                                                                                                                     [...]
 | `plugin.lettuce.trace_redis_parameters`                         | If set to true, the parameters of Redis commands would be collected by Lettuce agent.                                                                                                                                                                                                                                                                                                                                                    [...]
 | `plugin.lettuce.redis_parameter_max_length`                     | If set to positive number and `plugin.lettuce.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length.                                                                                                                                                                                                                                                                        [...]
+| `plugin.lettuce.operation_mapping_write`                        | Specify which command should be converted to `write` operation                                                                                                                                                                                                                                                                                                                                                                           [...]
+| `plugin.lettuce.operation_mapping_read  `                       | Specify which command should be converted to `read` operation                                                                                                                                                                                                                                                                                                                                                                            [...]
 | `plugin.jedis.trace_redis_parameters`                           | If set to true, the parameters of Redis commands would be collected by Jedis agent.                                                                                                                                                                                                                                                                                                                                                      [...]
 | `plugin.jedis.redis_parameter_max_length`                       | If set to positive number and `plugin.jedis.trace_redis_parameters` is set to `true`, Redis command parameters would be collected and truncated to this length.                                                                                                                                                                                                                                                                          [...]
 | `plugin.jedis.operation_mapping_write`                          | Specify which command should be converted to `write` operation                                                                                                                                                                                                                                                                                                                                                                           [...]
diff --git a/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml b/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml
index 2b0ada8f8a..43db792872 100644
--- a/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml
+++ b/test/plugin/scenarios/lettuce-scenario/config/expectedData.yaml
@@ -48,8 +48,10 @@ segmentItems:
             peer: not null
             skipAnalysis: false
             tags:
-              - {key: db.type, value: Redis}
-              - {key: db.statement, value: GET key<key>}
+              - {key: cache.type, value: Redis}
+              - {key: cache.key, value: key}
+              - {key: cache.cmd, value: GET}
+              - {key: cache.op, value: read}
           - operationName: Lettuce/SET
             parentSpanId: 0
             spanId: 2
@@ -62,8 +64,10 @@ segmentItems:
             peer: not null
             skipAnalysis: false
             tags:
-              - {key: db.type, value: Redis}
-              - {key: db.statement, value: SET key<key0> value<value0>}
+              - { key: cache.type, value: Redis }
+              - { key: cache.key, value: key0 }
+              - { key: cache.cmd, value: SET }
+              - { key: cache.op, value: write }
           - operationName: Lettuce/SET
             parentSpanId: 0
             spanId: 3
@@ -76,8 +80,10 @@ segmentItems:
             peer: not null
             skipAnalysis: false
             tags:
-              - {key: db.type, value: Redis}
-              - {key: db.statement, value: SET key<key1> value<value1>}
+              - { key: cache.type, value: Redis }
+              - { key: cache.key, value: key1 }
+              - { key: cache.cmd, value: SET }
+              - { key: cache.op, value: write }
           - operationName: GET:/lettuce-scenario/case/lettuce-case
             parentSpanId: -1
             spanId: 0