Skip to content

Commit f366503

Browse files
authored
Enhance lettuce plugin to adopt uniform tags (#441)
1 parent 8c989b7 commit f366503

7 files changed

Lines changed: 172 additions & 29 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Release Notes.
55
8.15.0
66
------------------
77

8-
8+
* Enhance lettuce plugin to adopt uniform tags.
99

1010
#### Documentation
1111

apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
2222

23+
import java.util.Arrays;
24+
import java.util.HashSet;
25+
import java.util.Set;
26+
2327
public class LettucePluginConfig {
2428
public static class Plugin {
2529
@PluginConfig(root = LettucePluginConfig.class)
@@ -36,6 +40,118 @@ public static class Lettuce {
3640
* Set a negative number to save specified length of parameter string to the tag.
3741
*/
3842
public static int REDIS_PARAMETER_MAX_LENGTH = 128;
43+
44+
/**
45+
* Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" usually
46+
* This config term define which command should be converted to write Operation .
47+
*
48+
* @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP
49+
* @see RedisChannelWriterInterceptor#parseOperation(String)
50+
*/
51+
public static Set<String> OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList(
52+
"getset",
53+
"set",
54+
"setbit",
55+
"setex ",
56+
"setnx ",
57+
"setrange",
58+
"strlen ",
59+
"mset",
60+
"msetnx ",
61+
"psetex",
62+
"incr ",
63+
"incrby ",
64+
"incrbyfloat",
65+
"decr ",
66+
"decrby ",
67+
"append ",
68+
"hmset",
69+
"hset",
70+
"hsetnx ",
71+
"hincrby",
72+
"hincrbyfloat",
73+
"hdel",
74+
"rpoplpush",
75+
"rpush",
76+
"rpushx",
77+
"lpush",
78+
"lpushx",
79+
"lrem",
80+
"ltrim",
81+
"lset",
82+
"brpoplpush",
83+
"linsert",
84+
"sadd",
85+
"sdiff",
86+
"sdiffstore",
87+
"sinterstore",
88+
"sismember",
89+
"srem",
90+
"sunion",
91+
"sunionstore",
92+
"sinter",
93+
"zadd",
94+
"zincrby",
95+
"zinterstore",
96+
"zrange",
97+
"zrangebylex",
98+
"zrangebyscore",
99+
"zrank",
100+
"zrem",
101+
"zremrangebylex",
102+
"zremrangebyrank",
103+
"zremrangebyscore",
104+
"zrevrange",
105+
"zrevrangebyscore",
106+
"zrevrank",
107+
"zunionstore",
108+
"xadd",
109+
"xdel",
110+
"del",
111+
"xtrim"
112+
));
113+
/**
114+
* Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" usually
115+
* This config term define which command should be converted to write Operation .
116+
*
117+
* @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP
118+
* @see RedisChannelWriterInterceptor#parseOperation(String)
119+
*/
120+
public static Set<String> OPERATION_MAPPING_READ = new HashSet<>(Arrays.asList(
121+
"getrange",
122+
"getbit ",
123+
"mget",
124+
"hvals",
125+
"hkeys",
126+
"hlen",
127+
"hexists",
128+
"hget",
129+
"hgetall",
130+
"hmget",
131+
"blpop",
132+
"brpop",
133+
"lindex",
134+
"llen",
135+
"lpop",
136+
"lrange",
137+
"rpop",
138+
"scard",
139+
"srandmember",
140+
"spop",
141+
"sscan",
142+
"smove",
143+
"zlexcount",
144+
"zscore",
145+
"zscan",
146+
"zcard",
147+
"zcount",
148+
"xget",
149+
"get",
150+
"xread",
151+
"xlen",
152+
"xrange",
153+
"xrevrange"
154+
));
39155
}
40156
}
41157
}

apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptor.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package org.apache.skywalking.apm.plugin.lettuce.v5;
2020

21+
import io.lettuce.core.codec.StringCodec;
2122
import io.lettuce.core.protocol.CommandArgs;
2223
import io.lettuce.core.protocol.DecoratedCommand;
2324
import io.lettuce.core.protocol.RedisCommand;
@@ -33,16 +34,18 @@
3334
import org.apache.skywalking.apm.util.StringUtil;
3435

3536
import java.lang.reflect.Method;
37+
import java.nio.ByteBuffer;
3638
import java.util.Collection;
3739
import java.util.List;
40+
import java.util.Optional;
3841

3942
@SuppressWarnings("unchecked")
4043
public class RedisChannelWriterInterceptor implements InstanceMethodsAroundInterceptor {
4144

4245
private static final String PASSWORD_MASK = "******";
4346
private static final String ABBR = "...";
44-
private static final String DELIMITER_SPACE = " ";
4547
private static final String AUTH = "AUTH";
48+
private static final StringCodec STRING_CODEC = new StringCodec();
4649

4750
@Override
4851
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) {
@@ -63,45 +66,48 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
6366
return;
6467
}
6568

66-
StringBuilder dbStatement = new StringBuilder();
6769
String operationName = "Lettuce/";
70+
String key = Constants.EMPTY_STRING;
71+
String command = Constants.EMPTY_STRING;
6872
if (allArguments[0] instanceof RedisCommand) {
6973
RedisCommand<?, ?, ?> redisCommand = (RedisCommand<?, ?, ?>) allArguments[0];
70-
String command = redisCommand.getType().name();
74+
command = redisCommand.getType().name();
7175
operationName = operationName + command;
72-
dbStatement.append(command);
7376
if (LettucePluginConfig.Plugin.Lettuce.TRACE_REDIS_PARAMETERS) {
74-
dbStatement.append(DELIMITER_SPACE).append(getArgsStatement(redisCommand));
77+
key = getArgsKey(redisCommand);
7578
}
7679
} else if (allArguments[0] instanceof Collection) {
77-
Collection<RedisCommand<?, ?, ?>> redisCommands = (Collection<RedisCommand<?, ?, ?>>) allArguments[0];
7880
operationName = operationName + "BATCH_WRITE";
79-
for (RedisCommand<?, ?, ?> redisCommand : redisCommands) {
80-
dbStatement.append(redisCommand.getType().name()).append(";");
81-
}
81+
command = "BATCH_WRITE";
8282
}
8383
AbstractSpan span = ContextManager.createExitSpan(operationName, peer);
8484
span.setComponent(ComponentsDefine.LETTUCE);
85-
Tags.DB_TYPE.set(span, "Redis");
86-
Tags.DB_STATEMENT.set(span, dbStatement.toString());
85+
Tags.CACHE_TYPE.set(span, "Redis");
86+
if (StringUtil.isNotEmpty(key)) {
87+
Tags.CACHE_KEY.set(span, key);
88+
}
89+
Tags.CACHE_CMD.set(span, command);
90+
parseOperation(command.toLowerCase()).ifPresent(op -> Tags.CACHE_OP.set(span, op));
8791
SpanLayer.asCache(span);
8892
span.prepareForAsync();
8993
ContextManager.stopSpan();
9094
enhancedCommand.setSkyWalkingDynamicField(span);
9195
}
9296

93-
private String getArgsStatement(RedisCommand<?, ?, ?> redisCommand) {
94-
String statement;
97+
private String getArgsKey(RedisCommand<?, ?, ?> redisCommand) {
9598
if (AUTH.equalsIgnoreCase(redisCommand.getType().name())) {
96-
statement = PASSWORD_MASK;
97-
} else {
98-
CommandArgs<?, ?> args = redisCommand.getArgs();
99-
statement = (args != null) ? args.toCommandString() : Constants.EMPTY_STRING;
99+
return PASSWORD_MASK;
100100
}
101-
if (StringUtil.isNotEmpty(statement) && statement.length() > LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) {
102-
statement = statement.substring(0, LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) + ABBR;
101+
CommandArgs<?, ?> args = redisCommand.getArgs();
102+
if (args == null) {
103+
return Constants.EMPTY_STRING;
104+
}
105+
ByteBuffer firstEncodedKey = args.getFirstEncodedKey();
106+
String key = STRING_CODEC.decodeKey(firstEncodedKey);
107+
if (StringUtil.isNotEmpty(key) && key.length() > LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) {
108+
key = StringUtil.cut(key, LettucePluginConfig.Plugin.Lettuce.REDIS_PARAMETER_MAX_LENGTH) + ABBR;
103109
}
104-
return statement;
110+
return key;
105111
}
106112

107113
@Override
@@ -144,4 +150,14 @@ public void handleMethodException(EnhancedInstance objInst, Method method, Objec
144150
}
145151
return command;
146152
}
153+
154+
private Optional<String> parseOperation(String cmd) {
155+
if (LettucePluginConfig.Plugin.Lettuce.OPERATION_MAPPING_READ.contains(cmd)) {
156+
return Optional.of("read");
157+
}
158+
if (LettucePluginConfig.Plugin.Lettuce.OPERATION_MAPPING_WRITE.contains(cmd)) {
159+
return Optional.of("write");
160+
}
161+
return Optional.empty();
162+
}
147163
}

apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/lettuce/v5/RedisChannelWriterInterceptorTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void setUp() {
121121

122122
@Test
123123
public void testInterceptor() {
124-
CommandArgs<?, ?> args = new CommandArgs<>(new ByteArrayCodec()).addKey("name".getBytes()).addValue("Tom".getBytes());
124+
CommandArgs<?, ?> args = new CommandArgs<>(new ByteArrayCodec()).addKey("name".getBytes());
125125
MockRedisCommand<?, ?, ?> redisCommand = new MockRedisCommand<>(CommandType.SET, null, args);
126126
interceptor.beforeMethod(mockRedisChannelWriterInstance, null, new Object[]{redisCommand}, null, null);
127127
interceptor.afterMethod(mockRedisChannelWriterInstance, null, null, null, null);
@@ -136,7 +136,6 @@ public void testInterceptor() {
136136
assertThat(SpanHelper.getComponentId(spans.get(0)), is(57));
137137
List<TagValuePair> tags = SpanHelper.getTags(spans.get(0));
138138
assertThat(tags.get(0).getValue(), is("Redis"));
139-
assertThat(tags.get(1).getValue(), CoreMatchers.containsString("Tom"));
140139
assertThat(SpanHelper.getLayer(spans.get(0)), CoreMatchers.is(SpanLayer.CACHE));
141140
assertThat(SpanHelper.getPeer(spans.get(0)), is(PEER));
142141
}

apm-sniffer/config/agent.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ plugin.toolkit.log.transmit_formatted=${SW_PLUGIN_TOOLKIT_LOG_TRANSMIT_FORMATTED
269269
plugin.lettuce.trace_redis_parameters=${SW_PLUGIN_LETTUCE_TRACE_REDIS_PARAMETERS:false}
270270
# 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.
271271
plugin.lettuce.redis_parameter_max_length=${SW_PLUGIN_LETTUCE_REDIS_PARAMETER_MAX_LENGTH:128}
272+
# Specify which command should be converted to write operation
273+
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,zremrangebyscore,zrevrange,zrevrangebyscore,zrevrank,zunionstore,xadd,xdel,del,xtrim}
274+
# Specify which command should be converted to read operation
275+
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}
272276
# If set to true, the parameters of the cypher would be collected.
273277
plugin.neo4j.trace_cypher_parameters=${SW_PLUGIN_NEO4J_TRACE_CYPHER_PARAMETERS:false}
274278
# 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.

0 commit comments

Comments
 (0)