如何实现业务链路监控

 

trace的一个demo版本,可以查看接口调用的整个链路,各种常规指标的统计,比如接口耗时,接口异常等。在此基础上手机日志后,可以做一些简单的logview展示。在实际工程中,根据业务需要,记录相关监控指标数据,可以在此基础上进行扩展,本demo展示的是trace链路原理上的处理方案。

 

1.注解类:对所有需要进行拦截的类添加该注解后,就会被扫描到做切面操作。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceAnno {
    String desc() default "";
}

2.trace拦截类:对于注解类切面后的操作,记录程序入口和接口整个链路

@Aspect
public class TraceApsect {

    private final static Logger logger = LoggerFactory.getLogger(TraceApsect.class);
    
    @Pointcut("@annotation(com.demo.it.trace.TraceAnno) " + "|| @within(com.demo.it.trace.TraceAnno)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object invoke(final ProceedingJoinPoint joinPoint) throws Throwable {
        if (!TraceSwitch.getInstance().isOpenProfilerTree()) {
            return joinPoint.proceed();
        }
        String methodName = this.getClassAndMethodName(joinPoint);
        if (null == methodName) {
            return joinPoint.proceed();
        }
        try {
            if (TraceProfiler.getEntry() == null) {
                TraceProfiler.start(methodName);
            } else {
                TraceProfiler.enter(methodName);
            }
            return joinPoint.proceed();
        } catch (Throwable e) {
            // 异常通知
            logger.error("The method " + methodName + " occurs expection : " + e);
            TraceProfiler.error(e);
            return e.getMessage();
        } finally {
            TraceProfiler.release();
            // 当root entry为状态为release的时候,打印信息,并做reset操作
            TraceProfiler.Entry rootEntry = TraceProfiler.getEntry();
            if (rootEntry != null) {
                if (rootEntry.isReleased()) {
                    long duration = rootEntry.getDuration();
                    if (duration > TraceSwitch.getInstance().getInvokeTimeout()) {
                        logger.warn(TraceProfiler.dump());
                    } else {
                        logger.info(TraceProfiler.dump());
                    }
                    TraceProfiler.reset();
                }
            }
        }
    }

    private String getClassAndMethodName(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature sign = (MethodSignature) joinPoint.getSignature();
            String clazzName = joinPoint.getTarget().toString();
            StringBuilder sb = new StringBuilder();
            sb.append(TraceProfiler.split(clazzName, "@")[0]);
            sb.append(":").append(sign.getMethod().getName());
            sb.append("(param:").append(sign.getMethod().getParameterTypes().length);
            sb.append(")");
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

3.trace 需要统计的内容:错误日志,耗时,ip, 接口调用连等

public final class TraceProfiler {
    /** 构建实体的存储缓存体 */
    private static final ThreadLocal<Entry> entryStack = new ThreadLocal<Entry>();

    /** 开始计时 */
    public static void start() {
        start((String) null);
    }

    /** 开始计时,创建一个Entry的实体对象 */
    public static void start(String message) {
        entryStack.set(new Entry(message, null, null));
    }

    /** threadLocal缓存清理,由于现在大多是线程池的设置,所以要做一个清理 */
    public static void reset() {
        entryStack.set(null);
    }

    /** 由于Entry自身是树状结构,所以如果是进入非Root的节点,那就需要enter来搞 */
    public static void enter(String message) {
        Entry currentEntry = getCurrentEntry();
        if (currentEntry != null) {
            currentEntry.enterSubEntry(message);
        }
    }
    
    /** 保存异常信息 */
    public static void error(Throwable e){
        Entry currentEntry = getCurrentEntry();
        if (currentEntry != null) {
            currentEntry.setErr(e);
        }
    }

    /** 方法运行结束之后,把当前的Entry的endTime来设置成当前时间 */
    public static void release() {
        Entry currentEntry = getCurrentEntry();
        if (currentEntry != null) {
            currentEntry.release();
        }
    }

    /** 获取start和end的时间差 */
    public static long getDuration() {
        Entry entry = (Entry) entryStack.get();
        if (entry != null) {
            return entry.getDuration();
        } else {
            return -1;
        }
    }

    /** 把Entry的信息dump出来,可以打印到日志中去 */
    public static String dump() {
        Entry entry = (Entry) entryStack.get();
        if (entry != null) {
            String str=JSONObject.toJSONString(entry);
            return str;
        } 
        return "";
    }

    /** 获取Entry信息 */
    public static Entry getEntry() {
        return (Entry) entryStack.get();
    }

    /** entry中含有subentry,如此这样来进行循环来保持树状的结构 */
    private static Entry getCurrentEntry() {
        Entry subEntry = (Entry) entryStack.get();
        Entry entry = null;
        if (subEntry != null) {
            do {
                entry = subEntry;
                subEntry = entry.getUnreleasedEntry();
            } while (subEntry != null);
        }
        return entry;
    }
    
    public static final class CONSTANT {
        public final static String DATE_FORMAT="yyyy-MM-dd hh:mm:ss SSS";
    }
    /**
     * 代表一个计时单元。
     */
    public static final class Entry {
        // subEntries来表示树状的子节点
        private final List<Entry> subEntries = new ArrayList<Entry>();
        private final Object message;
        private final Entry firstEntry;
        private final long startTime;
        private long endTime;
        private Throwable err;

        private Entry(Object message/* 描述信息 */, Entry parentEntry/* 父节点信息 */,
                Entry firstEntry/* 第一个节点 */) {
            this.message = message;
            this.startTime = TraceSwitch.getInstance().isOpenProfilerNanoTime() == true ? System
                    .nanoTime() : System.currentTimeMillis();
            this.firstEntry = (Entry) defaultIfNull(firstEntry, this);
        }

        /**
         * 取得entry的信息。
         */
        public String getMessage() {
            return defaultIfEmpty((String) message, null);
        }

        public static String defaultIfEmpty(String str, String defaultStr) {
            return ((str == null) || (str.length() == 0)) ? defaultStr : str;
        }

        public static Object defaultIfNull(Object object, Object defaultValue) {
            return (object != null) ? object : defaultValue;
        }

        /** 获取当前节点的开始时间 */
        public String getStartTime() {
            SimpleDateFormat fmt=new SimpleDateFormat(CONSTANT.DATE_FORMAT);
            return fmt.format(startTime)  ;
        }

        /** 获取当前节点的结束时间 */
        public String getEndTime() {
            SimpleDateFormat fmt=new SimpleDateFormat(CONSTANT.DATE_FORMAT);
            return fmt.format(endTime)  ;
        }

        /** 获取持续时间 */
        public long getDuration() {
            if (endTime < startTime) {
                return -1;
            } else {
                return endTime - startTime;
            }
        }

        /** 取得entry自身所用的时间,即总时间减去所有子entry所用的时间。 */
        public long getDurationOfSelf() {
            long duration = getDuration();
            if (duration < 0) {
                return -1;
            } else if (subEntries.isEmpty()) {
                return duration;
            } else {
                for (int i = 0; i < subEntries.size(); i++) {
                    Entry subEntry = (Entry) subEntries.get(i);
                    duration -= subEntry.getDuration();
                }
                if (duration < 0) {
                    return -1;
                } else {
                    return duration;
                }
            }
        }

        /** 取得所有子entries。 */
        public List<Entry> getSubEntries() {
            return Collections.unmodifiableList(subEntries);
        }

        /** 结束当前entry,并记录结束时间。 */
        private void release() {
            endTime = TraceSwitch.getInstance().isOpenProfilerNanoTime() == true ? System
                    .nanoTime() : System.currentTimeMillis();
        }

        /** 判断当前entry是否结束。 */
        public boolean isReleased() {
            return endTime > 0;
        }

        /** 创建一个新的子entry */
        private void enterSubEntry(Object message) {
            Entry subEntry = new Entry(message, this, firstEntry);
            subEntries.add(subEntry);
        }
        
        /** 添加异常信息 */
        public String getError() {
            return err==null?"":err.getMessage();
        }
        
        /** 设置异常信息 */
        public void setErr(Throwable e){
            this.err=e;
        }

        /** 取得未结束的子entry,链表中的最后一个元素 */
        private Entry getUnreleasedEntry() {
            Entry subEntry = null;
            if (!subEntries.isEmpty()) {
                subEntry = (Entry) subEntries.get(subEntries.size() - 1);
                if (subEntry.isReleased()) {
                    subEntry = null;
                }
            }
            return subEntry;
        }
    }

    public static String[] split(String str, String separatorChars) {
        return split(str, separatorChars, -1);
    }

    private static String[] split(String str, String separatorChars, int max) {
        if (str == null) {
            return null;
        }

        int length = str.length();

        if (length == 0) {
            return new String[0];
        }

        List<String> list = new LinkedList<String>();
        int sizePlus1 = 1;
        int i = 0;
        int start = 0;
        boolean match = false;

        if (separatorChars == null) {
            // null表示使用空白作为分隔符
            while (i < length) {
                if (Character.isWhitespace(str.charAt(i))) {
                    if (match) {
                        if (sizePlus1++ == max) {
                            i = length;
                        }

                        list.add(str.substring(start, i));
                        match = false;
                    }

                    start = ++i;
                    continue;
                }

                match = true;
                i++;
            }
        } else if (separatorChars.length() == 1) {
            
            char sep = separatorChars.charAt(0);

            while (i < length) {
                if (str.charAt(i) == sep) {
                    if (match) {
                        if (sizePlus1++ == max) {
                            i = length;
                        }

                        list.add(str.substring(start, i));
                        match = false;
                    }

                    start = ++i;
                    continue;
                }

                match = true;
                i++;
            }
        } else {
            // 一般情形
            while (i < length) {
                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                    if (match) {
                        if (sizePlus1++ == max) {
                            i = length;
                        }

                        list.add(str.substring(start, i));
                        match = false;
                    }

                    start = ++i;
                    continue;
                }

                match = true;
                i++;
            }
        }

        if (match) {
            list.add(str.substring(start, i));
        }
        return list.toArray(new String[list.size()]);
    }

}

4.开关,各种指标监控

public class TraceSwitch {

    private static TraceSwitch instance = new TraceSwitch();public static TraceSwitch getInstance(){
        return instance;
    }
    

    /**
     * 是否打开打印日志的开关
     */
    private boolean openProfilerTree =true;/**
     * 超时时间
     */
    private long invokeTimeout =500;/**
     * 是否打印纳秒
     * @return
     */
    private boolean openProfilerNanoTime = false;public boolean isOpenProfilerTree() {
        return openProfilerTree;
    }

    public void setOpenProfilerTree(boolean openProfilerTree) {
        this.openProfilerTree = openProfilerTree;
    }

    public long getInvokeTimeout() {
        return invokeTimeout;
    }

    public void setInvokeTimeout(long invokeTimeout) {
        this.invokeTimeout = invokeTimeout;
    }

    public boolean isOpenProfilerNanoTime() {
        return openProfilerNanoTime;
    }

    public void setOpenProfilerNanoTime(boolean openProfilerNanoTime) {
        this.openProfilerNanoTime = openProfilerNanoTime;
    }
    
    
}

 

posted @ 2016-11-03 10:07  路之石  阅读(4195)  评论(0编辑  收藏  举报