package net.dubboclub.catmonitor;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.remoting.RemotingException;
import com.alibaba.dubbo.remoting.TimeoutException;
import com.alibaba.dubbo.rpc.*;
import com.dianping.cat.Cat;
import com.dianping.cat.message.*;
import com.dianping.cat.message.internal.AbstractMessage;
import com.dianping.cat.message.spi.MessageTree;
import net.dubboclub.catmonitor.constants.CatConstants;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Created by bieber on 2015/11/4.
*/
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER},order = -9000)
public class CatTransaction implements Filter {
private final static String DUBBO_BIZ_ERROR="DUBBO_BIZ_ERROR";
private final static String DUBBO_TIMEOUT_ERROR="DUBBO_TIMEOUT_ERROR";
private final static String DUBBO_REMOTING_ERROR="DUBBO_REMOTING_ERROR";
private static final ThreadLocal<Cat.Context> CAT_CONTEXT = new ThreadLocal<Cat.Context>();
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if(!DubboCat.isEnable()){
Result result = invoker.invoke(invocation);
return result;
}
URL url = invoker.getUrl();
String sideKey = url.getParameter(Constants.SIDE_KEY);
String loggerName = invoker.getInterface().getSimpleName()+"."+invocation.getMethodName();
String type = CatConstants.CROSS_CONSUMER;
if(Constants.PROVIDER_SIDE.equals(sideKey)){
type= CatConstants.CROSS_SERVER;
}
Transaction transaction = Cat.newTransaction(type,loggerName);
Result result=null;
try{
Cat.Context context = getContext();
if(Constants.CONSUMER_SIDE.equals(sideKey)){
createConsumerCross(url,transaction);
Cat.logRemoteCallClient(context);
}else{
createProviderCross(url,transaction);
Cat.logRemoteCallServer(context);
}
setAttachment(context);
result = invoker.invoke(invocation);
if(result.hasException()){
//给调用接口出现异常进行打点
Throwable throwable = result.getException();
Event event = null;
if(RpcException.class==throwable.getClass()){
Throwable caseBy = throwable.getCause();
if(caseBy!=null&&caseBy.getClass()==TimeoutException.class){
event = Cat.newEvent(DUBBO_TIMEOUT_ERROR,loggerName);
}else{
event = Cat.newEvent(DUBBO_REMOTING_ERROR,loggerName);
}
}else if(RemotingException.class.isAssignableFrom(throwable.getClass())){
event = Cat.newEvent(DUBBO_REMOTING_ERROR,loggerName);
}else{
event = Cat.newEvent(DUBBO_BIZ_ERROR,loggerName);
}
event.setStatus(result.getException());
completeEvent(event);
transaction.addChild(event);
transaction.setStatus(result.getException().getClass().getSimpleName());
}else{
transaction.setStatus(Message.SUCCESS);
}
return result;
}catch (RuntimeException e){
Event event = null;
if(RpcException.class==e.getClass()){
Throwable caseBy = e.getCause();
if(caseBy!=null&&caseBy.getClass()==TimeoutException.class){
event = Cat.newEvent(DUBBO_TIMEOUT_ERROR,loggerName);
}else{
event = Cat.newEvent(DUBBO_REMOTING_ERROR,loggerName);
}
}else{
event = Cat.newEvent(DUBBO_BIZ_ERROR,loggerName);
}
event.setStatus(e);
completeEvent(event);
transaction.addChild(event);
transaction.setStatus(e.getClass().getSimpleName());
if(result==null){
throw e;
}else{
return result;
}
}finally {
transaction.complete();
CAT_CONTEXT.remove();
}
}
static class DubboCatContext implements Cat.Context{
private Map<String,String> properties = new HashMap<String, String>();
@Override
public void addProperty(String key, String value) {
properties.put(key,value);
}
@Override
public String getProperty(String key) {
return properties.get(key);
}
}
private String getProviderAppName(URL url){
String appName = url.getParameter(CatConstants.PROVIDER_APPLICATION_NAME);
if(StringUtils.isEmpty(appName)){
String interfaceName = url.getParameter(Constants.INTERFACE_KEY);
appName = interfaceName.substring(0,interfaceName.lastIndexOf('.'));
}
return appName;
}
private void setAttachment(Cat.Context context){
RpcContext.getContext().setAttachment(Cat.Context.ROOT,context.getProperty(Cat.Context.ROOT));
RpcContext.getContext().setAttachment(Cat.Context.CHILD,context.getProperty(Cat.Context.CHILD));
RpcContext.getContext().setAttachment(Cat.Context.PARENT,context.getProperty(Cat.Context.PARENT));
}
private Cat.Context getContext(){
Cat.Context context = CAT_CONTEXT.get();
if(context==null){
context = initContext();
CAT_CONTEXT.set(context);
}
return context;
}
private Cat.Context initContext(){
Cat.Context context = new DubboCatContext();
Map<String,String> attachments = RpcContext.getContext().getAttachments();
if(attachments!=null&&attachments.size()>0){
for(Map.Entry<String,String> entry:attachments.entrySet()){
if(Cat.Context.CHILD.equals(entry.getKey())||Cat.Context.ROOT.equals(entry.getKey())||Cat.Context.PARENT.equals(entry.getKey())){
context.addProperty(entry.getKey(),entry.getValue());
}
}
}
return context;
}
private void createConsumerCross(URL url,Transaction transaction){
Event crossAppEvent = Cat.newEvent(CatConstants.CONSUMER_CALL_APP,getProviderAppName(url));
Event crossServerEvent = Cat.newEvent(CatConstants.CONSUMER_CALL_SERVER,url.getHost());
Event crossPortEvent = Cat.newEvent(CatConstants.CONSUMER_CALL_PORT,url.getPort()+"");
crossAppEvent.setStatus(Event.SUCCESS);
crossServerEvent.setStatus(Event.SUCCESS);
crossPortEvent.setStatus(Event.SUCCESS);
completeEvent(crossAppEvent);
completeEvent(crossPortEvent);
completeEvent(crossServerEvent);
transaction.addChild(crossAppEvent);
transaction.addChild(crossPortEvent);
transaction.addChild(crossServerEvent);
}
private void completeEvent(Event event){
AbstractMessage message = (AbstractMessage) event;
message.setCompleted(true);
}
private void createProviderCross(URL url,Transaction transaction){
String consumerAppName = RpcContext.getContext().getAttachment(Constants.APPLICATION_KEY);
if(StringUtils.isEmpty(consumerAppName)){
consumerAppName= RpcContext.getContext().getRemoteHost()+":"+ RpcContext.getContext().getRemotePort();
}
Event crossAppEvent = Cat.newEvent(CatConstants.PROVIDER_CALL_APP,consumerAppName);
Event crossServerEvent = Cat.newEvent(CatConstants.PROVIDER_CALL_SERVER,url.getHost());
crossAppEvent.setStatus(Event.SUCCESS);
crossServerEvent.setStatus(Event.SUCCESS);
completeEvent(crossAppEvent);
completeEvent(crossServerEvent);
transaction.addChild(crossAppEvent);
transaction.addChild(crossServerEvent);
}
}