一個java的Profile工具

場景:咱們在系統運行中,須要監控某個代碼段的運行時間,咱們徹底可使用currentTimeMillis來作,可是作起來比較麻煩,尤爲是須要階段監控的時候,那麼這個工具就出現啦~~~java

先說下想要實現的功能:1.可以對代碼段進行運行時間的監控,好比代碼行a->代碼行b的運行時間。2.可以監控代碼行嵌套的運行時間監控,好比a->b->c->d中a->d和b->c的運行時間監控(相似括號同樣,造成配對的方式)。3.可以在運行範圍內的jvm一些指標的監控,好比內存使用量等。併發

/**
 * 性能相關的調試工具,支持,線程正式場景,作運行時間的profile,運行性能監控(不建議線上使用,由於須要開啓監控線程)
 * 該工具不會拋出任何異常
 * @author Administrator
 * @version $Id: ProfileUtils.java, v 0.1 2016年9月5日 下午11:02:45 Administrator Exp $
 */
public class Profiler {

    /** debug模式 */
    //    private static volatile boolean                    debug               = false;

    private final static String                        LOG_TEMPLATE        = "[messag=%s][startTime=%s][endTime=%s][durationTime=%sms][processors=%s][memUse=%s]";
    private final static String                        SIMPLE_LOG_TEMPLATE = "[durationTime=%sms][message=%s]";
    private final static SimpleDateFormat              DATE_FORMAT         = new SimpleDateFormat(
        "yyyy/MM/dd HH:mm:ss");

    /** profile日誌,建議運行中別作修改,不然有些配置會致使殘留線程 */
    private static ThreadLocal<ProfileConfig>          configHolder        = new ThreadLocal<ProfileConfig>() {
                                                                               protected ProfileConfig initialValue() {
                                                                                   return new ProfileConfig(
                                                                                       false, false,
                                                                                       0);
                                                                               };
                                                                           };

    /** 開始monitor的時間 */
    private static ThreadLocal<Stack<MonitorResource>> resStackHolder      = new ThreadLocal<Stack<MonitorResource>>() {
                                                                               protected java.util.Stack<MonitorResource> initialValue() {
                                                                                   return new Stack<MonitorResource>();
                                                                               };
                                                                           };

    /** 監控線程 */
    private static ThreadLocal<MonitorThread>          monitorThreadHolder = new ThreadLocal<MonitorThread>();

    /**
     * 開始monitor
     */
    public static void enter(Object msgObj) {
        try {
            Stack<MonitorResource> monitorResStack = resStackHolder.get();
            monitorResStack.push(new MonitorResource(msgObj, System.currentTimeMillis()));
            ProfileConfig config = configHolder.get();
            //開啓監控線程
            if (config.isUseMonitorThread()) {
                if (monitorThreadHolder.get() != null) {
                    killThread();
                }
                MonitorThread monitorThread = new MonitorThread(getCurrentMonitorRes(), config);
                monitorThreadHolder.set(monitorThread);
                monitorThread.start();
            }
        } catch (Throwable e) {
            //            if (debug) {
            //                e.printStackTrace();
            //            }
            return;
        }
    }

    /**
     * 結束monitor
     * @return
     */
    public static MonitorResource release() {
        try {
            Stack<MonitorResource> monitorResStack = resStackHolder.get();
            MonitorResource monitorResource = getCurrentMonitorRes();
            monitorResource.setEndTime(System.currentTimeMillis());
            ProfileConfig config = configHolder.get();
            //監控線程關閉
            if (config.isUseMonitorThread()) {
                killThread();
            }
            return monitorResStack.pop();
        } catch (Throwable e) {
            //            if (debug) {
            //                e.printStackTrace();
            //            }
            return new MonitorResource(e.getMessage(), 0);
        }
    }

    /**
     * 使用新的messageObj替換原來的
     * @param messageObj
     * @return
     */
    public static MonitorResource release(Object messageObj) {
        MonitorResource monitorResource = release();
        monitorResource.setMessageObj(messageObj);
        return monitorResource;
    }

    /**
     * 結束monitor而且打印日誌
     * @param logger
     * @return
     */
    public static MonitorResource releaseAndLog(Logger logger, Object messageObj) {
        MonitorResource resource = release(messageObj);
        LoggerUtils.info(logger, resource);
        return resource;
    }

    /**
     * 結束monitor而且打印日誌
     * @param logger
     * @return
     */
    public static MonitorResource releaseAndLog(Logger logger) {
        MonitorResource resource = release();
        LoggerUtils.info(logger, resource);
        return resource;
    }

    /**
     * 設置profile配置
     * @param config
     */
    public static void setProfileConfig(ProfileConfig config) {
        configHolder.set(config);
    }

    /**
     * Setter method for property <tt>debug</tt>.
     * 
     * @param debug value to be assigned to property debug
     */
    //    public static void setDebug(boolean debug) {
    //        Profiler.debug = debug;
    //    }

    /**
     * 移除監控線程
     */
    private static void killThread() {
        try {
            MonitorThread futureTask = monitorThreadHolder.get();
            monitorThreadHolder.remove();
            futureTask.interrupt();
        } catch (Throwable e) {
            // ignore
            //            if (debug) {
            //                e.printStackTrace();
            //            }
        }
    }

    /**
     * 獲取當前的monitorRes
     * @return
     */
    public static MonitorResource getCurrentMonitorRes() {
        try {
            Stack<MonitorResource> resStack = resStackHolder.get();
            return resStack.get(resStack.size() - 1);
        } catch (Exception e) {
            //            if (debug) {
            //                e.printStackTrace();
            //            }
            return new MonitorResource(e.getMessage(), 0);
        }
    }

    /**
     * 資源使用狀況,好比cpu最大使用量等。
     * @author Administrator
     * @version $Id: Profile.java, v 0.1 2016年9月5日 下午11:38:39 Administrator Exp $
     */
    public static class MonitorResource {

        /** 當前資源的標誌 */
        private Object     messageObj    = null;

        private long       startTime     = 0;

        private long       endTime       = 0;

        private int        processorNums = 0;

        private List<Long> memUse        = Lists.newArrayList();

        /**
         * @param messageObj
         * @param startTime
         */
        public MonitorResource(Object messageObj, long startTime) {
            super();
            this.messageObj = messageObj;
            this.startTime = startTime;
        }

        /**
         * Setter method for property <tt>messageObj</tt>.
         * 
         * @param messageObj value to be assigned to property messageObj
         */
        public void setMessageObj(Object messageObj) {
            this.messageObj = messageObj;
        }

        public String getMemUse() {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < memUse.size(); i++) {
                stringBuilder.append(memUse.get(i) / 1024L + "K");
                if (i != memUse.size() - 1) {
                    stringBuilder.append(",");
                }
            }
            return stringBuilder.toString();
        }

        /**
         * 獲取整個profile堆棧
         * @return
         */
        public Stack<MonitorResource> getMonitorResStack() {
            return resStackHolder.get();
        }

        /** 
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return configHolder.get().isUseSimpleLogTemplate()
                ? (String.format(SIMPLE_LOG_TEMPLATE, endTime - startTime, messageObj))
                : (String.format(LOG_TEMPLATE, messageObj, DATE_FORMAT.format(new Date(startTime)),
                    DATE_FORMAT.format(new Date(endTime)), endTime - startTime, processorNums,
                    getMemUse()));
        }

        /**
         * 獲取運行時間
         * @return
         */
        public long getDurTime() {
            return endTime - startTime;
        }

        public void putMemUse(long l) {
            memUse.add(l);
        }

        /**
         * Setter method for property <tt>endTime</tt>.
         * 
         * @param endTime value to be assigned to property endTime
         */
        public void setEndTime(long endTime) {
            this.endTime = endTime;
        }

        /**
         * Getter method for property <tt>messageObj</tt>.
         * 
         * @return property value of messageObj
         */
        public Object getMessageObj() {
            return messageObj;
        }

        /**
         * Setter method for property <tt>processorNums</tt>.
         * 
         * @param processorNums value to be assigned to property processorNums
         */
        public void setProcessorNums(int processorNums) {
            this.processorNums = processorNums;
        }

    }

    public static class ProfileConfig {
        private boolean useSimpleLogTemplate  = false;
        private boolean useMonitorThread      = false;
        private int     monitorCollectDurTime = 500;

        /**
         * @param useSimpleLogTemplate
         * @param useMonitorThread
         * @param monitorCollectDurTime
         */
        public ProfileConfig(boolean useSimpleLogTemplate, boolean useMonitorThread,
                             int monitorCollectDurTime) {
            super();
            this.useSimpleLogTemplate = useSimpleLogTemplate;
            this.useMonitorThread = useMonitorThread;
            this.monitorCollectDurTime = monitorCollectDurTime;
        }

        /**
         * Getter method for property <tt>useSimpleLogTemplate</tt>.
         * 
         * @return property value of useSimpleLogTemplate
         */
        public boolean isUseSimpleLogTemplate() {
            return useSimpleLogTemplate;
        }

        /**
         * Setter method for property <tt>useSimpleLogTemplate</tt>.
         * 
         * @param useSimpleLogTemplate value to be assigned to property useSimpleLogTemplate
         */
        public void setUseSimpleLogTemplate(boolean useSimpleLogTemplate) {
            this.useSimpleLogTemplate = useSimpleLogTemplate;
        }

        /**
         * Getter method for property <tt>useMonitorThread</tt>.
         * 
         * @return property value of useMonitorThread
         */
        public boolean isUseMonitorThread() {
            return useMonitorThread;
        }

        /**
         * Setter method for property <tt>useMonitorThread</tt>.
         * 
         * @param useMonitorThread value to be assigned to property useMonitorThread
         */
        public void setUseMonitorThread(boolean useMonitorThread) {
            this.useMonitorThread = useMonitorThread;
        }

        /**
         * Getter method for property <tt>monitorCollectDurTime</tt>.
         * 
         * @return property value of monitorCollectDurTime
         */
        public int getMonitorCollectDurTime() {
            return monitorCollectDurTime;
        }

        /**
         * Setter method for property <tt>monitorCollectDurTime</tt>.
         * 
         * @param monitorCollectDurTime value to be assigned to property monitorCollectDurTime
         */
        public void setMonitorCollectDurTime(int monitorCollectDurTime) {
            this.monitorCollectDurTime = monitorCollectDurTime;
        }

    }

    private static class MonitorThread extends Thread {

        private static final AtomicLong threadCount = new AtomicLong();

        private MonitorResource         monitorResource;

        private final ProfileConfig     config;

        /**
         * 
         */
        public MonitorThread(MonitorResource resource, ProfileConfig config) {
            monitorResource = resource;
            setName("monitor-thread-" + threadCount.getAndIncrement());
            setDaemon(true);
            this.config = config;
        }

        /** 
         * @see java.lang.Thread#run()
         */
        @Override
        public void run() {
            monitorResource.setProcessorNums(Runtime.getRuntime().availableProcessors());
            while (true) {
                monitorResource.putMemUse(
                    Runtime.getRuntime().maxMemory() - Runtime.getRuntime().freeMemory());

                try {
                    Thread.sleep(config.getMonitorCollectDurTime());
                } catch (InterruptedException e) {
                    //                    if (debug) {
                    //                        e.printStackTrace();
                    //                    }
                    return;
                }
            }
        }
    }
}

 能夠看到,咱們有個監控資源的概念,每一個階段都對應一個監控資源,好比a->d和b->c都對應了一個監控資源。從實現上,每次進入監控的時候,會生成一個監控資源,而且記錄當前時間,而且把該監控資源壓棧。同時,還會啓動一個監控線程,將jvm的狀態不斷寫入該監控資源中,因此針對a->d和b->c都生成了本身的監控線程,而且吧jvm狀態寫入本身的監控資源中。  當release的時候,會kill掉監控線程,而且把監控資源出棧。app

根據上面的設計能夠看到,該工具的使用須要注意下面的問題:jvm

1.若是不啓用監控線程,那麼能夠用於線上的場景。ide

2.若是啓用了監控線程,那麼只適合debug分析的場景,由於若是是線上,當監控的代碼塊併發量大起來的時候,會以1:1的比例建立監控線程,這個時候會有風險。工具

3.啓用監控線程的場景比較適合於,併發量較小(建立的監控線程少),可是執行時間長的場景。這個時候能夠對代碼塊進行執行分析。性能

相關文章
相關標籤/搜索