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}