如何實現業務鏈路監控


 

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;
    }
    
    
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM