001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.interceptor; 018 019import com.jamonapi.MonKey; 020import com.jamonapi.MonKeyImp; 021import com.jamonapi.Monitor; 022import com.jamonapi.MonitorFactory; 023import com.jamonapi.utils.Misc; 024import org.aopalliance.intercept.MethodInvocation; 025import org.apache.commons.logging.Log; 026 027/** 028 * Performance monitor interceptor that uses <b>JAMon</b> library to perform the 029 * performance measurement on the intercepted method and output the stats. 030 * In addition, it tracks/counts exceptions thrown by the intercepted method. 031 * The stack traces can be viewed in the JAMon web application. 032 * 033 * <p>This code is inspired by Thierry Templier's blog. 034 * 035 * @author Dmitriy Kopylenko 036 * @author Juergen Hoeller 037 * @author Rob Harrop 038 * @author Steve Souza 039 * @since 1.1.3 040 * @see com.jamonapi.MonitorFactory 041 * @see PerformanceMonitorInterceptor 042 */ 043@SuppressWarnings("serial") 044public class JamonPerformanceMonitorInterceptor extends AbstractMonitoringInterceptor { 045 046 private boolean trackAllInvocations = false; 047 048 049 /** 050 * Create a new JamonPerformanceMonitorInterceptor with a static logger. 051 */ 052 public JamonPerformanceMonitorInterceptor() { 053 } 054 055 /** 056 * Create a new JamonPerformanceMonitorInterceptor with a dynamic or static logger, 057 * according to the given flag. 058 * @param useDynamicLogger whether to use a dynamic logger or a static logger 059 * @see #setUseDynamicLogger 060 */ 061 public JamonPerformanceMonitorInterceptor(boolean useDynamicLogger) { 062 setUseDynamicLogger(useDynamicLogger); 063 } 064 065 /** 066 * Create a new JamonPerformanceMonitorInterceptor with a dynamic or static logger, 067 * according to the given flag. 068 * @param useDynamicLogger whether to use a dynamic logger or a static logger 069 * @param trackAllInvocations whether to track all invocations that go through 070 * this interceptor, or just invocations with trace logging enabled 071 * @see #setUseDynamicLogger 072 */ 073 public JamonPerformanceMonitorInterceptor(boolean useDynamicLogger, boolean trackAllInvocations) { 074 setUseDynamicLogger(useDynamicLogger); 075 setTrackAllInvocations(trackAllInvocations); 076 } 077 078 079 /** 080 * Set whether to track all invocations that go through this interceptor, 081 * or just invocations with trace logging enabled. 082 * <p>Default is "false": Only invocations with trace logging enabled will 083 * be monitored. Specify "true" to let JAMon track all invocations, 084 * gathering statistics even when trace logging is disabled. 085 */ 086 public void setTrackAllInvocations(boolean trackAllInvocations) { 087 this.trackAllInvocations = trackAllInvocations; 088 } 089 090 091 /** 092 * Always applies the interceptor if the "trackAllInvocations" flag has been set; 093 * else just kicks in if the log is enabled. 094 * @see #setTrackAllInvocations 095 * @see #isLogEnabled 096 */ 097 @Override 098 protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger) { 099 return (this.trackAllInvocations || isLogEnabled(logger)); 100 } 101 102 /** 103 * Wraps the invocation with a JAMon Monitor and writes the current 104 * performance statistics to the log (if enabled). 105 * @see com.jamonapi.MonitorFactory#start 106 * @see com.jamonapi.Monitor#stop 107 */ 108 @Override 109 protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable { 110 String name = createInvocationTraceName(invocation); 111 MonKey key = new MonKeyImp(name, name, "ms."); 112 113 Monitor monitor = MonitorFactory.start(key); 114 try { 115 return invocation.proceed(); 116 } 117 catch (Throwable ex) { 118 trackException(key, ex); 119 throw ex; 120 } 121 finally { 122 monitor.stop(); 123 if (!this.trackAllInvocations || isLogEnabled(logger)) { 124 writeToLog(logger, "JAMon performance statistics for method [" + name + "]:\n" + monitor); 125 } 126 } 127 } 128 129 /** 130 * Count the thrown exception and put the stack trace in the details portion of the key. 131 * This will allow the stack trace to be viewed in the JAMon web application. 132 */ 133 protected void trackException(MonKey key, Throwable ex) { 134 String stackTrace = "stackTrace=" + Misc.getExceptionTrace(ex); 135 key.setDetails(stackTrace); 136 137 // Specific exception counter. Example: java.lang.RuntimeException 138 MonitorFactory.add(new MonKeyImp(ex.getClass().getName(), stackTrace, "Exception"), 1); 139 140 // General exception counter which is a total for all exceptions thrown 141 MonitorFactory.add(new MonKeyImp(MonitorFactory.EXCEPTIONS_LABEL, stackTrace, "Exception"), 1); 142 } 143 144}