UtilTimerStatck类:用于记录代码执行的时间工具类
ProfilingTimerBean类:javabean类,包含一些startTime,totalTime,resource,parent,children等属性
用于时间的记录以及堆栈的实现。
使用步骤:
1.struts2配置文件配置:<constant name="struts.devMode" value="true"/>
2.在指定的action中进行配置:
<interceptor-ref name="profiling"> <param name="profilingKey">profiling</param> </interceptor-ref>
3.访问时例如:http://localhost:8000/StrutsStudy/action001.do?profiling=true
结果:
[2000ms] - EncodingFilter_doFilter: [2000ms] - FilterDispatcher_doFilter: [2000ms] - Handling request from Dispatcher [0ms] - create DefaultActionProxy: [0ms] - create DefaultActionInvocation: [0ms] - actionCreate: action001 [2000ms] - invoke: [2000ms] - interceptor: profiling [2000ms] - invoke: [2000ms] - invokeAction: action001 [0ms] - executeResult: success
源码如下:
UtilTimerStack类:
public class UtilTimerStack { // A reference to the current ProfilingTimerBean protected static ThreadLocal<ProfilingTimerBean> current = new ThreadLocal<ProfilingTimerBean>(); /** * System property that controls whether this timer should be used or not. Set to "true" activates * the timer. Set to "false" to disactivate. */ public static final String ACTIVATE_PROPERTY = "xwork.profile.activate"; /** * System property that controls the min time, that if exceeded will cause a log (at INFO level) to be * created. */ public static final String MIN_TIME = "xwork.profile.mintime"; private static final Log log = LogFactory.getLog(UtilTimerStack.class); /** * Create and start a performance profiling with the <code>name</code> given. Deal with * profile hierarchy automatically, so caller don't have to be concern about it. * * * * * * @param name profile name */ public static void push(String name) { //判断该诊断工具是否激活 if (!isActive()) return; //create a new timer and start it //本地线程变量-相关时间配置bean ProfilingTimerBean newTimer = new ProfilingTimerBean(name); newTimer.setStartTime(); //if there is a current timer - add the new timer as a child of it //如果当前线程未绑定,那么进行绑定,否则直接获取 ProfilingTimerBean currentTimer = (ProfilingTimerBean) current.get(); if (currentTimer != null) { //当且仅当继续使用本地线程并同时调用push操作时,即添加其儿子节点,并同时添加当前儿子节点的父节点 /** *很给力的设计:更改currentTimer(bean)的内容(添加子节点),同时将该父亲bean添加到newTimer中去 * 因此我觉得bean的设计不是树的结构,而是堆栈的思路 * public void addChild(ProfilingTimerBean child){ children.add(child); child.addParent(this); } */ currentTimer.addChild(newTimer); } //set the new timer to be the current timer //设置新的线程变量 current.set(newTimer); } /** * End a preformance profiling with the <code>name</code> given. Deal with * profile hierarchy automatically, so caller don't have to be concern about it. * * @param name profile name */ public static void pop(String name) { if (!isActive()) return; //获取本地线程变量bean ProfilingTimerBean currentTimer = (ProfilingTimerBean) current.get(); //if the timers are matched up with each other (ie push("a"); pop("a")); if (currentTimer != null && name != null && name.equals(currentTimer.getResource())) { //设置本地线程变量bean的终结时间 currentTimer.setEndTime(); ProfilingTimerBean parent = currentTimer.getParent(); //if we are the root timer, then print out the times if (parent == null) { //且只有当线程变量的父节点为null时,才会答应 printTimes(currentTimer); current.set(null); //for those servers that use thread pooling } else { current.set(parent); } } else { //if timers are not matched up, then print what we have, and then print warning. if (currentTimer != null) { printTimes(currentTimer); current.set(null); //prevent printing multiple times log.warn("Unmatched Timer. Was expecting " + currentTimer.getResource() + ", instead got " + name); } } } /** * Do a log (at INFO level) of the time taken for this particular profiling. * * @param currentTimer profiling timer bean */ private static void printTimes(ProfilingTimerBean currentTimer) { log.info(currentTimer.getPrintable(getMinTime())); } /** * Get the min time for this profiling, it searches for a System property * 'xwork.profile.mintime' and default to 0. * * @return long */ private static long getMinTime() { try { return Long.parseLong(System.getProperty(MIN_TIME, "0")); } catch (NumberFormatException e) { return -1; } } /** * Determine if profiling is being activated, by searching for a system property * 'xwork.profile.activate', default to false (profiling is off). * * @return <tt>true</tt>, if active, <tt>false</tt> otherwise. */ public static boolean isActive() { return System.getProperty(ACTIVATE_PROPERTY) != null; } /** * Turn profiling on or off. * 诊断工具开启与否的设计,通过System.setProperty * @param active */ public static void setActive(boolean active) { if (active) System.setProperty(ACTIVATE_PROPERTY, "true"); else System.clearProperty(ACTIVATE_PROPERTY); } /** * A convenience method that allows <code>block</code> of code subjected to profiling to be executed * and avoid the need of coding boiler code that does pushing (UtilTimeBean.push(...)) and * poping (UtilTimerBean.pop(...)) in a try ... finally ... block. * * <p/> * * Example of usage: * <pre> * // we need a returning result * String result = UtilTimerStack.profile("purchaseItem: ", * new UtilTimerStack.ProfilingBlock<String>() { * public String doProfiling() { * getMyService().purchaseItem(....) * return "Ok"; * } * }); * </pre> * or * <pre> * // we don't need a returning result * UtilTimerStack.profile("purchaseItem: ", * new UtilTimerStack.ProfilingBlock<String>() { * public String doProfiling() { * getMyService().purchaseItem(....) * return null; * } * }); * </pre> * * @param <T> any return value if there's one. * @param name profile name * @param block code block subjected to profiling * @return T * @throws Exception */ public static <T> T profile(String name, ProfilingBlock<T> block) throws Exception { UtilTimerStack.push(name); try { return block.doProfiling(); } finally { UtilTimerStack.pop(name); } } /** * A callback interface where code subjected to profile is to be executed. This eliminates the need * of coding boiler code that does pushing (UtilTimerBean.push(...)) and poping (UtilTimerBean.pop(...)) * in a try ... finally ... block. * * @version $Date$ $Id$ * * @param <T> */ public static interface ProfilingBlock<T> { /** * Method that execute the code subjected to profiling. * * @return profiles Type * @throws Exception */ T doProfiling() throws Exception; } }
ProfilingTimerBean类:这个类比较容易看懂,因此未加任何注释。
public class ProfilingTimerBean implements java.io.Serializable { private static final long serialVersionUID = -6180672043920208784L; List<ProfilingTimerBean> children = new ArrayList<ProfilingTimerBean>(); ProfilingTimerBean parent = null; String resource; long startTime; long totalTime; public ProfilingTimerBean(String resource) { this.resource = resource; } protected void addParent(ProfilingTimerBean parent) { this.parent = parent; } public ProfilingTimerBean getParent() { return parent; } public void addChild(ProfilingTimerBean child) { children.add(child); child.addParent(this); } public void setStartTime() { this.startTime = System.currentTimeMillis(); } public void setEndTime() { this.totalTime = System.currentTimeMillis() - startTime; } public String getResource() { return resource; } /** * Get a formatted string representing all the methods that took longer than a specified time. */ public String getPrintable(long minTime) { return getPrintable("", minTime); } protected String getPrintable(String indent, long minTime) { //only print the value if we are larger or equal to the min time. if (totalTime >= minTime) { StringBuffer buffer = new StringBuffer(); buffer.append(indent); buffer.append("[" + totalTime + "ms] - " + resource); buffer.append("\n"); Iterator childrenIt = children.iterator(); while (childrenIt.hasNext()) { buffer.append(((ProfilingTimerBean) childrenIt.next()).getPrintable(indent + " ", minTime)); } return buffer.toString(); } else return ""; } }
1.栈的设计思路
2.ThreadLocal的理解使用(同步访问解决的新思路)
3.设置启动与否的新思路(System.property)
4.log4g的简单使用