001 package org.apache.fulcrum.yaafi.interceptor.logging;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.lang.reflect.Method;
023
024 import org.apache.avalon.framework.activity.Initializable;
025 import org.apache.avalon.framework.configuration.Configuration;
026 import org.apache.avalon.framework.configuration.ConfigurationException;
027 import org.apache.avalon.framework.configuration.Reconfigurable;
028 import org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext;
029 import org.apache.fulcrum.yaafi.framework.reflection.Clazz;
030 import org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl;
031 import org.apache.fulcrum.yaafi.interceptor.util.DefaultToStringBuilderImpl;
032 import org.apache.fulcrum.yaafi.interceptor.util.InterceptorToStringBuilder;
033 import org.apache.fulcrum.yaafi.interceptor.util.MethodToStringBuilderImpl;
034 import org.apache.fulcrum.yaafi.interceptor.util.ArgumentToStringBuilderImpl;
035 import org.apache.fulcrum.yaafi.interceptor.util.StopWatch;
036
037 /**
038 * A service logging of service invocations. The service allows to monitor
039 * a list of services defined in the configuration.
040 *
041 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
042 */
043
044 public class LoggingInterceptorServiceImpl
045 extends BaseInterceptorServiceImpl
046 implements LoggingInterceptorService, Reconfigurable, Initializable
047 {
048 /** the maximum length of a dumped argument */
049 private static final int MAX_ARG_LENGTH = 2000;
050
051 /** seperator for the arguments in the logfile */
052 private static final String SEPERATOR = ";";
053
054 /** maximum argument length for dumping arguments */
055 private int maxArgLength;
056
057 /** the class name of the string builder to use */
058 private String toStringBuilderClassName;
059
060 /** monitor all excpetions independent from the monitored services */
061 private boolean monitorAllExceptions;
062
063 /** the ReflectionToStringBuilder class */
064 private Class toStringBuilderClass;
065
066 /////////////////////////////////////////////////////////////////////////
067 // Avalon Service Lifecycle Implementation
068 /////////////////////////////////////////////////////////////////////////
069
070 /**
071 * Constructor
072 */
073 public LoggingInterceptorServiceImpl()
074 {
075 super();
076 this.maxArgLength = MAX_ARG_LENGTH;
077 }
078
079 /**
080 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
081 */
082 public void configure(Configuration configuration) throws ConfigurationException
083 {
084 super.configure(configuration);
085
086 this.maxArgLength = configuration.getChild("maxArgLength").getValueAsInteger(MAX_ARG_LENGTH);
087 this.toStringBuilderClassName = configuration.getChild("toStringBuilderClass").getValue(ArgumentToStringBuilderImpl.class.getName());
088 this.monitorAllExceptions = configuration.getChild("monitorAllExceptions").getValueAsBoolean(true);
089 }
090
091 /**
092 * @see org.apache.avalon.framework.activity.Initializable#initialize()
093 */
094 public void initialize() throws Exception
095 {
096 // load the string builder class
097
098 ClassLoader classLoader = this.getClass().getClassLoader();
099
100 if( Clazz.hasClazz(classLoader, this.getToStringBuilderClassName()) )
101 {
102 this.toStringBuilderClass = Clazz.getClazz(
103 classLoader,
104 this.getToStringBuilderClassName()
105 );
106 }
107
108 // create an instance of the StringBuilder to see if everything works
109
110 InterceptorToStringBuilder interceptorToStringBuilder = this.createArgumentToStringBuilder(
111 this
112 );
113
114 interceptorToStringBuilder.toString();
115 }
116
117 /**
118 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
119 */
120 public void reconfigure(Configuration configuration) throws ConfigurationException
121 {
122 super.reconfigure(configuration);
123 this.configure(configuration);
124 }
125
126 /////////////////////////////////////////////////////////////////////////
127 // Service interface implementation
128 /////////////////////////////////////////////////////////////////////////
129
130 /**
131 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onEntry(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext)
132 */
133 public void onEntry(AvalonInterceptorContext interceptorContext)
134 {
135 if( this.isServiceMonitored(interceptorContext ) )
136 {
137 if( this.getLogger().isInfoEnabled() )
138 {
139 String msg = this.toString(interceptorContext,null,ON_ENTRY);
140 this.getLogger().info(msg);
141 this.createStopWatch(interceptorContext);
142 }
143 }
144 }
145
146 /**
147 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onError(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Throwable)
148 */
149 public void onError(AvalonInterceptorContext interceptorContext,Throwable t)
150 {
151 if( this.getLogger().isErrorEnabled() )
152 {
153 if( this.isMonitorAllExceptions() || this.isServiceMonitored(interceptorContext) )
154 {
155 StopWatch stopWatch = this.getStopWatch(interceptorContext);
156 stopWatch.stop();
157 String msg = this.toString(interceptorContext, stopWatch, t);
158 this.getLogger().error(msg);
159 }
160 }
161 }
162
163 /**
164 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onExit(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Object)
165 */
166 public void onExit(AvalonInterceptorContext interceptorContext, Object result)
167 {
168 if( this.isServiceMonitored(interceptorContext) )
169 {
170 if( this.getLogger().isDebugEnabled() )
171 {
172 StopWatch stopWatch = this.getStopWatch(interceptorContext);
173 stopWatch.stop();
174 String msg = this.toString(interceptorContext, stopWatch, result);
175 this.getLogger().debug(msg);
176 }
177 }
178 }
179
180 /////////////////////////////////////////////////////////////////////////
181 // Service Implementation
182 /////////////////////////////////////////////////////////////////////////
183
184 /**
185 * Creates a stop watch
186 *
187 * @param interceptorContext the current interceptor context
188 */
189 protected void createStopWatch(
190 AvalonInterceptorContext interceptorContext )
191 {
192 StopWatch stopWatch = new StopWatch();
193 stopWatch.start();
194 interceptorContext.getRequestContext().put(this.getServiceName(),stopWatch);
195 }
196
197 /**
198 * Gets the stop watch. Even if none is defined we return one
199 * in a proper state.
200 *
201 * @param interceptorContext the current interceptor context
202 * @return the stop watch
203 */
204 protected StopWatch getStopWatch(
205 AvalonInterceptorContext interceptorContext )
206 {
207 StopWatch result = (StopWatch) interceptorContext.getRequestContext().remove(
208 this.getServiceName()
209 );
210
211 if( result == null )
212 {
213 result = new StopWatch();
214 result.start();
215 }
216
217 return result;
218 }
219
220 /**
221 * @return Returns the maxLineLength.
222 */
223 protected int getMaxArgLength()
224 {
225 return maxArgLength;
226 }
227
228 /**
229 * @return Returns the monitorAllExceptions.
230 */
231 protected boolean isMonitorAllExceptions()
232 {
233 return monitorAllExceptions;
234 }
235
236 /**
237 * @return Returns the toStringBuilderClass.
238 */
239 protected Class getToStringBuilderClass()
240 {
241 return toStringBuilderClass;
242 }
243
244 /**
245 * @return Returns the toStringBuilderClassName.
246 */
247 protected String getToStringBuilderClassName()
248 {
249 return toStringBuilderClassName;
250 }
251
252 /**
253 * Create an instance of an InterceptorToStringBuilder
254 *
255 * @param target the object to stringify
256 * @return the string builder
257 */
258 protected InterceptorToStringBuilder createArgumentToStringBuilder(Object target)
259 {
260 InterceptorToStringBuilder result = null;
261
262 try
263 {
264 result = (InterceptorToStringBuilder)
265 this.getToStringBuilderClass().newInstance();
266 }
267 catch (Exception e)
268 {
269 String msg = "Unable to create an instance for " + this.getToStringBuilderClassName();
270 this.getLogger().error(msg,e);
271 result = new DefaultToStringBuilderImpl();
272 }
273
274 result.setTarget(target);
275 result.setMaxArgLength(this.getMaxArgLength());
276 result.setMode(1);
277
278 return result;
279 }
280
281 /**
282 * Create a string representation of a service invocation returning a result.
283 *
284 * @param avalonInterceptorContext the interceptor context
285 * @param stopWatch the stopwatch for the execution time
286 * @param result the result of the service invocation
287 * @return the string representation of the result
288 */
289 protected String toString(
290 AvalonInterceptorContext avalonInterceptorContext,
291 StopWatch stopWatch,
292 Object result )
293 {
294 StringBuffer methodSignature = new StringBuffer();
295 InterceptorToStringBuilder toStringBuilder = this.createArgumentToStringBuilder(result);
296
297 methodSignature.append( this.toString(avalonInterceptorContext, stopWatch, ON_EXIT) );
298 methodSignature.append(SEPERATOR);
299 methodSignature.append( "result={" );
300 methodSignature.append( toStringBuilder.toString() );
301 methodSignature.append( "}" );
302
303 return methodSignature.toString();
304 }
305
306 /**
307 * Create a string representation of a service invocation throwing a Throwable
308 *
309 * @param avalonInterceptorContext the interceptor context
310 * @param stopWatch the stopwatch for the execution time
311 * @param throwable the result of the service invocation
312 * @return the string representation of the result
313 */
314 protected String toString(
315 AvalonInterceptorContext avalonInterceptorContext,
316 StopWatch stopWatch,
317 Throwable throwable )
318 {
319 StringBuffer methodSignature = new StringBuffer();
320 InterceptorToStringBuilder toStringBuilder = this.createArgumentToStringBuilder(throwable);
321
322 methodSignature.append( this.toString(avalonInterceptorContext, stopWatch, ON_ERROR) );
323 methodSignature.append(SEPERATOR);
324 methodSignature.append( throwable.getClass().getName() );
325 methodSignature.append(SEPERATOR);
326 methodSignature.append( toStringBuilder.toString() );
327
328 return methodSignature.toString();
329 }
330
331 /**
332 * Create a method signature.
333 *
334 * @param interceptorContext the avalonInterceptorContext
335 * @param stopWatch the stopwatch for the execution time
336 * @param mode the mode (onEntry, onExit, onError)
337 * @return the debug output
338 */
339 protected String toString(
340 AvalonInterceptorContext interceptorContext, StopWatch stopWatch, int mode )
341 {
342 StringBuffer result = new StringBuffer();
343 Method method = interceptorContext.getMethod();
344 Object[] args = interceptorContext.getArgs();
345 InterceptorToStringBuilder toStringBuilder = null;
346 MethodToStringBuilderImpl methodToStringBuilder = new MethodToStringBuilderImpl(method);
347
348 if( args == null )
349 {
350 args = new Object[0];
351 }
352
353 result.append(interceptorContext.getTransactionId());
354 result.append(SEPERATOR);
355 result.append(interceptorContext.getInvocationId());
356 result.append(SEPERATOR);
357 result.append(interceptorContext.getInvocationDepth());
358 result.append(SEPERATOR);
359 result.append(mode);
360 result.append(SEPERATOR);
361 result.append(interceptorContext.getServiceShorthand());
362 result.append(SEPERATOR);
363 result.append(method.getName());
364 result.append(SEPERATOR);
365
366 if( stopWatch != null )
367 {
368 result.append(stopWatch.getTime());
369 }
370 else
371 {
372 result.append('0');
373 }
374
375 result.append(SEPERATOR);
376 result.append(methodToStringBuilder.toString());
377
378 if( (ON_ENTRY == mode) || (ON_ERROR == mode) )
379 {
380 for( int i=0; i<args.length; i++ )
381 {
382 toStringBuilder = this.createArgumentToStringBuilder(args[i]);
383 result.append(SEPERATOR);
384 result.append("arg[" + i + "]:={");
385 result.append( toStringBuilder.toString());
386 result.append("}");
387 }
388 }
389
390 return result.toString();
391 }
392 }