Enhance lettuce plugin to adopt uniform tags (#441)
This commit is contained in:
parent
8c989b74e9
commit
f366503d58
|
|
@ -5,7 +5,7 @@ Release Notes.
|
|||
8.15.0
|
||||
------------------
|
||||
|
||||
|
||||
* Enhance lettuce plugin to adopt uniform tags.
|
||||
|
||||
#### Documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,zremrangebyscore,zrevrange,zrevrangebyscore,zrevrank,zunionstore,xadd,xdel,del,xtrim}
|
||||
# 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.
|
||||
|
|
|
|||
|
|
@ -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. | SW_PLUGIN_TOOLKIT_LOG_TRANSMIT_FORMATTED | `true` |
|
||||
| `plugin.lettuce.trace_redis_parameters` | If set to true, the parameters of Redis commands would be collected by Lettuce agent. | SW_PLUGIN_LETTUCE_TRACE_REDIS_PARAMETERS | `false` |
|
||||
| `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. | SW_PLUGIN_LETTUCE_REDIS_PARAMETER_MAX_LENGTH | `128` |
|
||||
| `plugin.lettuce.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_LETTUCE_OPERATION_MAPPING_WRITE | |
|
||||
| `plugin.lettuce.operation_mapping_read ` | Specify which command should be converted to `read` operation | SW_PLUGIN_LETTUCE_OPERATION_MAPPING_READ | Referenc [Lettuce-5.x-plugin](https://github.com/apache/skywalking-java/blob/main/apm-sniffer/apm-sdk-plugin/lettuce-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/lettuce/v5/LettucePluginConfig.java) |
|
||||
| `plugin.jedis.trace_redis_parameters` | If set to true, the parameters of Redis commands would be collected by Jedis agent. | SW_PLUGIN_JEDIS_TRACE_REDIS_PARAMETERS | `false` |
|
||||
| `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. | SW_PLUGIN_JEDIS_REDIS_PARAMETER_MAX_LENGTH | `128` |
|
||||
| `plugin.jedis.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_JEDIS_OPERATION_MAPPING_WRITE | |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue