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    }