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