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