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 java.io.Serializable; 020 021import org.aopalliance.intercept.MethodInterceptor; 022import org.aopalliance.intercept.MethodInvocation; 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 025 026import org.springframework.aop.support.AopUtils; 027 028/** 029 * Base {@code MethodInterceptor} implementation for tracing. 030 * 031 * <p>By default, log messages are written to the log for the interceptor class, 032 * not the class which is being intercepted. Setting the {@code useDynamicLogger} 033 * bean property to {@code true} causes all log messages to be written to 034 * the {@code Log} for the target class being intercepted. 035 * 036 * <p>Subclasses must implement the {@code invokeUnderTrace} method, which 037 * is invoked by this class ONLY when a particular invocation SHOULD be traced. 038 * Subclasses should write to the {@code Log} instance provided. 039 * 040 * @author Rob Harrop 041 * @author Juergen Hoeller 042 * @since 1.2 043 * @see #setUseDynamicLogger 044 * @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log) 045 */ 046@SuppressWarnings("serial") 047public abstract class AbstractTraceInterceptor implements MethodInterceptor, Serializable { 048 049 /** 050 * The default {@code Log} instance used to write trace messages. 051 * This instance is mapped to the implementing {@code Class}. 052 */ 053 protected transient Log defaultLogger = LogFactory.getLog(getClass()); 054 055 /** 056 * Indicates whether or not proxy class names should be hidden when using dynamic loggers. 057 * @see #setUseDynamicLogger 058 */ 059 private boolean hideProxyClassNames = false; 060 061 /** 062 * Indicates whether to pass an exception to the logger. 063 * @see #writeToLog(Log, String, Throwable) 064 */ 065 private boolean logExceptionStackTrace = true; 066 067 068 /** 069 * Set whether to use a dynamic logger or a static logger. 070 * Default is a static logger for this trace interceptor. 071 * <p>Used to determine which {@code Log} instance should be used to write 072 * log messages for a particular method invocation: a dynamic one for the 073 * {@code Class} getting called, or a static one for the {@code Class} 074 * of the trace interceptor. 075 * <p><b>NOTE:</b> Specify either this property or "loggerName", not both. 076 * @see #getLoggerForInvocation(org.aopalliance.intercept.MethodInvocation) 077 */ 078 public void setUseDynamicLogger(boolean useDynamicLogger) { 079 // Release default logger if it is not being used. 080 this.defaultLogger = (useDynamicLogger ? null : LogFactory.getLog(getClass())); 081 } 082 083 /** 084 * Set the name of the logger to use. The name will be passed to the 085 * underlying logger implementation through Commons Logging, getting 086 * interpreted as log category according to the logger's configuration. 087 * <p>This can be specified to not log into the category of a class 088 * (whether this interceptor's class or the class getting called) 089 * but rather into a specific named category. 090 * <p><b>NOTE:</b> Specify either this property or "useDynamicLogger", not both. 091 * @see org.apache.commons.logging.LogFactory#getLog(String) 092 * @see org.apache.log4j.Logger#getLogger(String) 093 * @see java.util.logging.Logger#getLogger(String) 094 */ 095 public void setLoggerName(String loggerName) { 096 this.defaultLogger = LogFactory.getLog(loggerName); 097 } 098 099 /** 100 * Set to "true" to have {@link #setUseDynamicLogger dynamic loggers} hide 101 * proxy class names wherever possible. Default is "false". 102 */ 103 public void setHideProxyClassNames(boolean hideProxyClassNames) { 104 this.hideProxyClassNames = hideProxyClassNames; 105 } 106 107 /** 108 * Set whether to pass an exception to the logger, suggesting inclusion 109 * of its stack trace into the log. Default is "true"; set this to "false" 110 * in order to reduce the log output to just the trace message (which may 111 * include the exception class name and exception message, if applicable). 112 * @since 4.3.10 113 */ 114 public void setLogExceptionStackTrace(boolean logExceptionStackTrace) { 115 this.logExceptionStackTrace = logExceptionStackTrace; 116 } 117 118 119 /** 120 * Determines whether or not logging is enabled for the particular {@code MethodInvocation}. 121 * If not, the method invocation proceeds as normal, otherwise the method invocation is passed 122 * to the {@code invokeUnderTrace} method for handling. 123 * @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log) 124 */ 125 @Override 126 public Object invoke(MethodInvocation invocation) throws Throwable { 127 Log logger = getLoggerForInvocation(invocation); 128 if (isInterceptorEnabled(invocation, logger)) { 129 return invokeUnderTrace(invocation, logger); 130 } 131 else { 132 return invocation.proceed(); 133 } 134 } 135 136 /** 137 * Return the appropriate {@code Log} instance to use for the given 138 * {@code MethodInvocation}. If the {@code useDynamicLogger} flag 139 * is set, the {@code Log} instance will be for the target class of the 140 * {@code MethodInvocation}, otherwise the {@code Log} will be the 141 * default static logger. 142 * @param invocation the {@code MethodInvocation} being traced 143 * @return the {@code Log} instance to use 144 * @see #setUseDynamicLogger 145 */ 146 protected Log getLoggerForInvocation(MethodInvocation invocation) { 147 if (this.defaultLogger != null) { 148 return this.defaultLogger; 149 } 150 else { 151 Object target = invocation.getThis(); 152 return LogFactory.getLog(getClassForLogging(target)); 153 } 154 } 155 156 /** 157 * Determine the class to use for logging purposes. 158 * @param target the target object to introspect 159 * @return the target class for the given object 160 * @see #setHideProxyClassNames 161 */ 162 protected Class<?> getClassForLogging(Object target) { 163 return (this.hideProxyClassNames ? AopUtils.getTargetClass(target) : target.getClass()); 164 } 165 166 /** 167 * Determine whether the interceptor should kick in, that is, 168 * whether the {@code invokeUnderTrace} method should be called. 169 * <p>Default behavior is to check whether the given {@code Log} 170 * instance is enabled. Subclasses can override this to apply the 171 * interceptor in other cases as well. 172 * @param invocation the {@code MethodInvocation} being traced 173 * @param logger the {@code Log} instance to check 174 * @see #invokeUnderTrace 175 * @see #isLogEnabled 176 */ 177 protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger) { 178 return isLogEnabled(logger); 179 } 180 181 /** 182 * Determine whether the given {@link Log} instance is enabled. 183 * <p>Default is {@code true} when the "trace" level is enabled. 184 * Subclasses can override this to change the level under which 'tracing' occurs. 185 * @param logger the {@code Log} instance to check 186 */ 187 protected boolean isLogEnabled(Log logger) { 188 return logger.isTraceEnabled(); 189 } 190 191 /** 192 * Write the supplied trace message to the supplied {@code Log} instance. 193 * <p>To be called by {@link #invokeUnderTrace} for enter/exit messages. 194 * <p>Delegates to {@link #writeToLog(Log, String, Throwable)} as the 195 * ultimate delegate that controls the underlying logger invocation. 196 * @since 4.3.10 197 * @see #writeToLog(Log, String, Throwable) 198 */ 199 protected void writeToLog(Log logger, String message) { 200 writeToLog(logger, message, null); 201 } 202 203 /** 204 * Write the supplied trace message and {@link Throwable} to the 205 * supplied {@code Log} instance. 206 * <p>To be called by {@link #invokeUnderTrace} for enter/exit outcomes, 207 * potentially including an exception. Note that an exception's stack trace 208 * won't get logged when {@link #setLogExceptionStackTrace} is "false". 209 * <p>By default messages are written at {@code TRACE} level. Subclasses 210 * can override this method to control which level the message is written 211 * at, typically also overriding {@link #isLogEnabled} accordingly. 212 * @since 4.3.10 213 * @see #setLogExceptionStackTrace 214 * @see #isLogEnabled 215 */ 216 protected void writeToLog(Log logger, String message, Throwable ex) { 217 if (ex != null && this.logExceptionStackTrace) { 218 logger.trace(message, ex); 219 } 220 else { 221 logger.trace(message); 222 } 223 } 224 225 226 /** 227 * Subclasses must override this method to perform any tracing around the 228 * supplied {@code MethodInvocation}. Subclasses are responsible for 229 * ensuring that the {@code MethodInvocation} actually executes by 230 * calling {@code MethodInvocation.proceed()}. 231 * <p>By default, the passed-in {@code Log} instance will have log level 232 * "trace" enabled. Subclasses do not have to check for this again, unless 233 * they overwrite the {@code isInterceptorEnabled} method to modify 234 * the default behavior, and may delegate to {@code writeToLog} for actual 235 * messages to be written. 236 * @param logger the {@code Log} to write trace messages to 237 * @return the result of the call to {@code MethodInvocation.proceed()} 238 * @throws Throwable if the call to {@code MethodInvocation.proceed()} 239 * encountered any errors 240 * @see #isLogEnabled 241 * @see #writeToLog(Log, String) 242 * @see #writeToLog(Log, String, Throwable) 243 */ 244 protected abstract Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable; 245 246}