package org.skywalking.apm.plugin.jedis.v2;
import org.skywalking.apm.agent.core.context.ContextManager;
import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
import org.skywalking.apm.agent.core.plugin.interceptor.assist.NoConcurrencyAccessObject;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.skywalking.apm.util.StringUtil;
import org.skywalking.apm.trace.Span;
import org.skywalking.apm.trace.tag.Tags;
/**
* {@link JedisMethodInterceptor} intercept all method of {@link redis.clients.jedis.Jedis}
* or {@link redis.clients.jedis.JedisCluster}. {@link JedisMethodInterceptor} record
* the redis host, operation name and the key of the operation.
*
* @author zhangxin
*/
public class JedisMethodInterceptor extends NoConcurrencyAccessObject implements InstanceMethodsAroundInterceptor {
/**
* The key name that redis connection information in {@link EnhancedClassInstanceContext#context}.
*/
protected static final String KEY_OF_REDIS_CONN_INFO = "REDIS_CONNECTION_INFO";
/**
* The key name that multiple redis hosts in {@link EnhancedClassInstanceContext#context}.
*/
protected static final String KEY_OF_REDIS_HOSTS = "KEY_OF_REDIS_HOSTS";
/**
* The key name that redis host in {@link EnhancedClassInstanceContext#context}.
* it will be null if the value that fetch from {@link EnhancedClassInstanceContext#context}
* by using {@link #KEY_OF_REDIS_HOSTS} is not null.
*/
protected static final String KEY_OF_REDIS_HOST = "KEY_OF_REDIS_HOST";
/**
* The key name that redis port in {@link EnhancedClassInstanceContext#context}.
* It can not be null if the value that fetch from {@link EnhancedClassInstanceContext#context} by
* using {@link #KEY_OF_REDIS_HOST} is not null.
*/
protected static final String KEY_OF_REDIS_PORT = "KEY_OF_REDIS_PORT";
private static final String REDIS_COMPONENT = "Redis";
@Override
public void beforeMethod(final EnhancedClassInstanceContext context,
final InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) {
this.whenEnter(context, interceptorContext);
}
/**
* set peer host information for the current active span.
*/
private void tagPeer(Span span, EnhancedClassInstanceContext context) {
String redisHosts = (String) context.get(KEY_OF_REDIS_HOSTS);
if (!StringUtil.isEmpty(redisHosts)) {
Tags.PEERS.set(span, (String) context.get(KEY_OF_REDIS_HOSTS));
} else {
Tags.PEER_HOST.set(span, (String) context.get(KEY_OF_REDIS_HOST));
Tags.PEER_PORT.set(span, (Integer) context.get(KEY_OF_REDIS_PORT));
}
}
@Override
public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
Object ret) {
this.whenExist(context);
return ret;
}
@Override
public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
InstanceMethodInvokeContext interceptorContext) {
ContextManager.activeSpan().log(t);
}
@Override
protected void enter(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext) {
Span span = ContextManager.createSpan("Jedis/" + interceptorContext.methodName());
Tags.COMPONENT.set(span, REDIS_COMPONENT);
Tags.DB_TYPE.set(span, REDIS_COMPONENT);
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
tagPeer(span, context);
Tags.SPAN_LAYER.asDB(span);
if (StringUtil.isEmpty(context.get(KEY_OF_REDIS_HOST, String.class))) {
Tags.PEERS.set(span, String.valueOf(context.get(KEY_OF_REDIS_HOSTS)));
} else {
Tags.PEER_HOST.set(span, context.get(KEY_OF_REDIS_HOST, String.class));
Tags.PEER_PORT.set(span, (Integer) context.get(KEY_OF_REDIS_PORT));
}
if (interceptorContext.allArguments().length > 0
&& interceptorContext.allArguments()[0] instanceof String) {
Tags.DB_STATEMENT.set(span, interceptorContext.methodName() + " " + interceptorContext.allArguments()[0]);
}
}
@Override
protected void exit() {
ContextManager.stopSpan();
}
}