View Javadoc
1   package org.apache.fulcrum.yaafi.interceptor.logging;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.Method;
23  
24  import org.apache.avalon.framework.activity.Initializable;
25  import org.apache.avalon.framework.configuration.Configuration;
26  import org.apache.avalon.framework.configuration.ConfigurationException;
27  import org.apache.avalon.framework.configuration.Reconfigurable;
28  import org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext;
29  import org.apache.fulcrum.yaafi.framework.reflection.Clazz;
30  import org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl;
31  import org.apache.fulcrum.yaafi.interceptor.util.ArgumentToStringBuilderImpl;
32  import org.apache.fulcrum.yaafi.interceptor.util.DefaultToStringBuilderImpl;
33  import org.apache.fulcrum.yaafi.interceptor.util.InterceptorToStringBuilder;
34  import org.apache.fulcrum.yaafi.interceptor.util.MethodToStringBuilderImpl;
35  import org.apache.fulcrum.yaafi.interceptor.util.StopWatch;
36  
37  /**
38   * A service logging of service invocations. The service allows to monitor a
39   * list of services defined in the configuration.
40   *
41   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
42   */
43  
44  public class LoggingInterceptorServiceImpl extends BaseInterceptorServiceImpl
45  		implements LoggingInterceptorService, Reconfigurable, Initializable {
46  	/** the maximum length of a dumped argument */
47  	private static final int MAX_ARG_LENGTH = 2000;
48  
49  	/** seperator for the arguments in the logfile */
50  	private static final String SEPERATOR = ";";
51  
52  	/** maximum argument length for dumping arguments */
53  	private int maxArgLength;
54  
55  	/** the class name of the string builder to use */
56  	private String toStringBuilderClassName;
57  
58  	/** monitor all excpetions independent from the monitored services */
59  	private boolean monitorAllExceptions;
60  
61  	/** the ReflectionToStringBuilder class */
62  	private Class<?> toStringBuilderClass;
63  
64  	/////////////////////////////////////////////////////////////////////////
65  	// Avalon Service Lifecycle Implementation
66  	/////////////////////////////////////////////////////////////////////////
67  
68  	/**
69  	 * Constructor
70  	 */
71  	public LoggingInterceptorServiceImpl() {
72  		super();
73  		this.maxArgLength = MAX_ARG_LENGTH;
74  	}
75  
76  	/**
77  	 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
78  	 */
79  	public void configure(Configuration configuration) throws ConfigurationException {
80  		super.configure(configuration);
81  
82  		this.maxArgLength = configuration.getChild("maxArgLength").getValueAsInteger(MAX_ARG_LENGTH);
83  		this.toStringBuilderClassName = configuration.getChild("toStringBuilderClass")
84  				.getValue(ArgumentToStringBuilderImpl.class.getName());
85  		this.monitorAllExceptions = configuration.getChild("monitorAllExceptions").getValueAsBoolean(true);
86  	}
87  
88  	/**
89  	 * @see org.apache.avalon.framework.activity.Initializable#initialize()
90  	 */
91  	public void initialize() throws Exception {
92  		// load the string builder class
93  
94  		ClassLoader classLoader = this.getClass().getClassLoader();
95  
96  		if (Clazz.hasClazz(classLoader, this.getToStringBuilderClassName())) {
97  			this.toStringBuilderClass = Clazz.getClazz(classLoader, this.getToStringBuilderClassName());
98  		}
99  
100 		// create an instance of the StringBuilder to see if everything works
101 
102 		InterceptorToStringBuilder interceptorToStringBuilder = this.createArgumentToStringBuilder(this);
103 
104 		interceptorToStringBuilder.toString();
105 	}
106 
107 	/**
108 	 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
109 	 */
110 	public void reconfigure(Configuration configuration) throws ConfigurationException {
111 		super.reconfigure(configuration);
112 		this.configure(configuration);
113 	}
114 
115 	/////////////////////////////////////////////////////////////////////////
116 	// Service interface implementation
117 	/////////////////////////////////////////////////////////////////////////
118 
119 	/*
120 	 * (non-Javadoc)
121 	 * 
122 	 * @see
123 	 * org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#
124 	 * onEntry(org.apache.fulcrum.yaafi.framework.interceptor.
125 	 * AvalonInterceptorContext)
126 	 */
127 	public void onEntry(AvalonInterceptorContext interceptorContext) {
128 		if (this.isServiceMonitored(interceptorContext) && this.getLogger().isInfoEnabled() == true) {
129 			String msg = this.toString(interceptorContext, null, ON_ENTRY);
130 			this.getLogger().info(msg);
131 			this.createStopWatch(interceptorContext);
132 		}
133 	}
134 
135 	/* (non-Javadoc)
136 	 * @see org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#onError(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Throwable)
137 	 */
138 	public void onError(AvalonInterceptorContext interceptorContext, Throwable t) {
139 		if (this.getLogger().isErrorEnabled()
140 				&& (this.isMonitorAllExceptions() || this.isServiceMonitored(interceptorContext))) {
141 			StopWatch stopWatch = this.getStopWatch(interceptorContext);
142 			stopWatch.stop();
143 			String msg = this.toString(interceptorContext, stopWatch, t);
144 			this.getLogger().error(msg);
145 		}
146 	}
147 
148 	/* (non-Javadoc)
149 	 * @see org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#onExit(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Object)
150 	 */
151 	public void onExit(AvalonInterceptorContext interceptorContext, Object result) {
152 		if (this.isServiceMonitored(interceptorContext) && this.getLogger().isDebugEnabled() == true) {
153 			StopWatch stopWatch = this.getStopWatch(interceptorContext);
154 			stopWatch.stop();
155 			String msg = this.toString(interceptorContext, stopWatch, result);
156 			this.getLogger().debug(msg);
157 		}
158 	}
159 
160 	/////////////////////////////////////////////////////////////////////////
161 	// Service Implementation
162 	/////////////////////////////////////////////////////////////////////////
163 
164 	/**
165 	 * Creates a stop watch
166 	 *
167 	 * @param interceptorContext the current interceptor context
168 	 */
169 	protected void createStopWatch(AvalonInterceptorContext interceptorContext) {
170 		StopWatch stopWatch = new StopWatch();
171 		stopWatch.start();
172 		interceptorContext.getRequestContext().put(this.getServiceName(), stopWatch);
173 	}
174 
175 	/**
176 	 * Gets the stop watch. Even if none is defined we return one in a proper state.
177 	 *
178 	 * @param interceptorContext the current interceptor context
179 	 * @return the stop watch
180 	 */
181 	protected StopWatch getStopWatch(AvalonInterceptorContext interceptorContext) {
182 		StopWatch result = (StopWatch) interceptorContext.getRequestContext().remove(this.getServiceName());
183 
184 		if (result == null) {
185 			result = new StopWatch();
186 			result.start();
187 		}
188 
189 		return result;
190 	}
191 
192 	/**
193 	 * @return Returns the maxLineLength.
194 	 */
195 	protected int getMaxArgLength() {
196 		return maxArgLength;
197 	}
198 
199 	/**
200 	 * @return Returns the monitorAllExceptions.
201 	 */
202 	protected boolean isMonitorAllExceptions() {
203 		return monitorAllExceptions;
204 	}
205 
206 	/**
207 	 * @return Returns the toStringBuilderClass.
208 	 */
209 	protected Class<?> getToStringBuilderClass() {
210 		return toStringBuilderClass;
211 	}
212 
213 	/**
214 	 * @return Returns the toStringBuilderClassName.
215 	 */
216 	protected String getToStringBuilderClassName() {
217 		return toStringBuilderClassName;
218 	}
219 
220 	/**
221 	 * Create an instance of an InterceptorToStringBuilder
222 	 *
223 	 * @param target the object to stringify
224 	 * @return the string builder
225 	 */
226 	protected InterceptorToStringBuilder createArgumentToStringBuilder(Object target) {
227 		InterceptorToStringBuilder result = null;
228 
229 		try {
230 			result = (InterceptorToStringBuilder) this.getToStringBuilderClass().newInstance();
231 		} catch (Exception e) {
232 			String msg = "Unable to create an instance for " + this.getToStringBuilderClassName();
233 			this.getLogger().error(msg, e);
234 			result = new DefaultToStringBuilderImpl();
235 		}
236 
237 		result.setTarget(target);
238 		result.setMaxArgLength(this.getMaxArgLength());
239 		result.setMode(1);
240 
241 		return result;
242 	}
243 
244 	/**
245 	 * Create a string representation of a service invocation returning a result.
246 	 *
247 	 * @param avalonInterceptorContext the interceptor context
248 	 * @param stopWatch                the stopwatch for the execution time
249 	 * @param result                   the result of the service invocation
250 	 * @return the string representation of the result
251 	 */
252 	protected String toString(AvalonInterceptorContext avalonInterceptorContext, StopWatch stopWatch, Object result) {
253 		StringBuilder methodSignature = new StringBuilder();
254 		InterceptorToStringBuilder toStringBuilder = this.createArgumentToStringBuilder(result);
255 
256 		methodSignature.append(this.toString(avalonInterceptorContext, stopWatch, ON_EXIT));
257 		methodSignature.append(SEPERATOR);
258 		methodSignature.append("result={");
259 		methodSignature.append(toStringBuilder.toString());
260 		methodSignature.append("}");
261 
262 		return methodSignature.toString();
263 	}
264 
265 	/**
266 	 * Create a string representation of a service invocation throwing a Throwable
267 	 *
268 	 * @param avalonInterceptorContext the interceptor context
269 	 * @param stopWatch                the stopwatch for the execution time
270 	 * @param throwable                the result of the service invocation
271 	 * @return the string representation of the result
272 	 */
273 	protected String toString(AvalonInterceptorContext avalonInterceptorContext, StopWatch stopWatch,
274 			Throwable throwable) {
275 		StringBuilder methodSignature = new StringBuilder();
276 		InterceptorToStringBuilder toStringBuilder = this.createArgumentToStringBuilder(throwable);
277 
278 		methodSignature.append(this.toString(avalonInterceptorContext, stopWatch, ON_ERROR));
279 		methodSignature.append(SEPERATOR);
280 		methodSignature.append(throwable.getClass().getName());
281 		methodSignature.append(SEPERATOR);
282 		methodSignature.append(toStringBuilder.toString());
283 
284 		return methodSignature.toString();
285 	}
286 
287 	/**
288 	 * Create a method signature.
289 	 *
290 	 * @param interceptorContext the avalonInterceptorContext
291 	 * @param stopWatch          the stopwatch for the execution time
292 	 * @param mode               the mode (onEntry, onExit, onError)
293 	 * @return the debug output
294 	 */
295 	protected String toString(AvalonInterceptorContext interceptorContext, StopWatch stopWatch, int mode) {
296 		StringBuilder result = new StringBuilder();
297 		Method method = interceptorContext.getMethod();
298 		Object[] args = interceptorContext.getArgs();
299 		InterceptorToStringBuilder toStringBuilder = null;
300 		MethodToStringBuilderImpl methodToStringBuilder = new MethodToStringBuilderImpl(method);
301 
302 		if (args == null) {
303 			args = new Object[0];
304 		}
305 
306 		result.append(interceptorContext.getTransactionId());
307 		result.append(SEPERATOR);
308 		result.append(interceptorContext.getInvocationId());
309 		result.append(SEPERATOR);
310 		result.append(interceptorContext.getInvocationDepth());
311 		result.append(SEPERATOR);
312 		result.append(mode);
313 		result.append(SEPERATOR);
314 		result.append(interceptorContext.getServiceShorthand());
315 		result.append(SEPERATOR);
316 		result.append(method.getName());
317 		result.append(SEPERATOR);
318 
319 		if (stopWatch != null) {
320 			result.append(stopWatch.getTime());
321 		} else {
322 			result.append('0');
323 		}
324 
325 		result.append(SEPERATOR);
326 		result.append(methodToStringBuilder.toString());
327 
328 		if (ON_ENTRY == mode || ON_ERROR == mode) {
329 			for (int i = 0; i < args.length; i++) {
330 				toStringBuilder = this.createArgumentToStringBuilder(args[i]);
331 				result.append(SEPERATOR);
332 				result.append("arg[" + i + "]:={");
333 				result.append(toStringBuilder.toString());
334 				result.append("}");
335 			}
336 		}
337 
338 		return result.toString();
339 	}
340 }