001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.processor.interceptor;
018    
019    import java.util.List;
020    
021    import org.apache.camel.CamelContext;
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.LoggingLevel;
024    import org.apache.camel.Predicate;
025    import org.apache.camel.Processor;
026    import org.apache.camel.Service;
027    import org.apache.camel.model.ProcessorDefinition;
028    import org.apache.camel.processor.Logger;
029    import org.apache.camel.spi.ExchangeFormatter;
030    import org.apache.camel.spi.InterceptStrategy;
031    import org.apache.commons.logging.LogFactory;
032    
033    /**
034     * An interceptor strategy for tracing routes
035     *
036     * @version $Revision: 836213 $
037     */
038    public class Tracer implements InterceptStrategy, Service {
039    
040        private TraceFormatter formatter = new DefaultTraceFormatter();
041        private boolean enabled = true;
042        private String logName = Tracer.class.getName();
043        private LoggingLevel logLevel = LoggingLevel.INFO;
044        private Predicate traceFilter;
045        private boolean traceInterceptors;
046        private boolean traceExceptions = true;
047        private boolean logStackTrace;
048        private boolean traceOutExchanges;
049        private String destinationUri;
050        private Endpoint destination;
051        private boolean useJpa;
052        private Logger logger;
053    
054        /**
055         * Creates a new tracer.
056         *
057         * @param context Camel context
058         * @return a new tracer
059         */
060        public static Tracer createTracer(CamelContext context) {
061            Tracer tracer = new Tracer();
062            // lets see if we have a formatter if so use it
063            TraceFormatter formatter = context.getRegistry().lookup("traceFormatter", TraceFormatter.class);
064            if (formatter != null) {
065                tracer.setFormatter(formatter);
066            }
067            return tracer;
068        }
069    
070        /**
071         * A helper method to return the Tracer instance if one is enabled
072         *
073         * @return the tracer or null if none can be found
074         */
075        public static Tracer getTracer(CamelContext context) {
076            List<InterceptStrategy> list = context.getInterceptStrategies();
077            for (InterceptStrategy interceptStrategy : list) {
078                if (interceptStrategy instanceof Tracer) {
079                    return (Tracer)interceptStrategy;
080                }
081            }
082            return null;
083        }
084    
085        /**
086         * Gets the logger to be used for tracers that can format and log a given exchange.
087         *
088         * @param formatter the exchange formatter
089         * @return the logger to use
090         */
091        public synchronized Logger getLogger(ExchangeFormatter formatter) {
092            if (logger == null) {
093                logger = new Logger(LogFactory.getLog(getLogName()), formatter);
094                logger.setLevel(getLogLevel());
095            }
096            return logger;
097        }
098    
099        public Processor wrapProcessorInInterceptors(CamelContext context, ProcessorDefinition<?> definition,
100                                                     Processor target, Processor nextTarget) throws Exception {
101    
102            // Force the creation of an id, otherwise the id is not available when the trace formatter is
103            // outputting trace information
104            definition.idOrCreate(context.getNodeIdFactory());
105            return new TraceInterceptor(definition, target, formatter, this);
106        }
107    
108        public TraceFormatter getFormatter() {
109            return formatter;
110        }
111    
112        public DefaultTraceFormatter getDefaultTraceFormatter() {
113            if (formatter instanceof DefaultTraceFormatter) {
114                return (DefaultTraceFormatter) formatter;
115            }
116            return null;
117        }
118    
119        public void setFormatter(TraceFormatter formatter) {
120            this.formatter = formatter;
121        }
122    
123        public void setEnabled(boolean flag) {
124            enabled = flag;
125        }
126    
127        public boolean isEnabled() {
128            return enabled;
129        }
130    
131        public boolean isTraceInterceptors() {
132            return traceInterceptors;
133        }
134    
135        /**
136         * Sets whether interceptors should be traced or not
137         */
138        public void setTraceInterceptors(boolean traceInterceptors) {
139            this.traceInterceptors = traceInterceptors;
140        }
141    
142        public Predicate getTraceFilter() {
143            return traceFilter;
144        }
145    
146        /**
147         * Sets a predicate to be used as filter when tracing
148         */
149        public void setTraceFilter(Predicate traceFilter) {
150            this.traceFilter = traceFilter;
151        }
152    
153        public LoggingLevel getLogLevel() {
154            return logLevel;
155        }
156    
157        /**
158         * Sets the logging level to output tracing. Will use <tt>INFO</tt> level by default.
159         */
160        public void setLogLevel(LoggingLevel logLevel) {
161            this.logLevel = logLevel;
162            // update logger if its in use
163            if (logger != null) {
164                logger.setLevel(logLevel);
165            }
166        }
167    
168        public boolean isTraceExceptions() {
169            return traceExceptions;
170        }
171    
172        /**
173         * Sets whether thrown exceptions should be traced
174         */
175        public void setTraceExceptions(boolean traceExceptions) {
176            this.traceExceptions = traceExceptions;
177        }
178    
179        public boolean isLogStackTrace() {
180            return logStackTrace;
181        }
182    
183        /**
184         * Sets whether thrown exception stacktrace should be traced, if disabled then only the exception message is logged
185         */
186        public void setLogStackTrace(boolean logStackTrace) {
187            this.logStackTrace = logStackTrace;
188        }
189    
190        public String getLogName() {
191            return logName;
192        }
193    
194        /**
195         * Sets the logging name to use.
196         * Will default use <tt>org.apache.camel.processor.interceptor.TraceInterceptor<tt>.
197         */
198        public void setLogName(String logName) {
199            this.logName = logName;
200            // update logger if its in use
201            if (logger != null) {
202                logger.setLogName(logName);
203            }
204        }
205    
206        /**
207         * Sets whether exchanges coming out of processors should be traced
208         */    
209        public void setTraceOutExchanges(boolean traceOutExchanges) {
210            this.traceOutExchanges = traceOutExchanges;
211        }
212        
213        public boolean isTraceOutExchanges() {
214            return traceOutExchanges;
215        }
216    
217        public String getDestinationUri() {
218            return destinationUri;
219        }
220    
221        /**
222         * Sets an optional destination to send the traced Exchange.
223         * <p/>
224         * Can be used to store tracing as files, in a database or whatever. The routing of the Exchange
225         * will happen synchronously and the original route will first continue when this destination routing
226         * has been completed.
227         */
228        public void setDestinationUri(String destinationUri) {
229            this.destinationUri = destinationUri;
230        }
231    
232        public Endpoint getDestination() {
233            return destination;
234        }
235    
236        /**
237         * See {@link #setDestinationUri(String)}
238         */
239        public void setDestination(Endpoint destination) {
240            this.destination = destination;
241        }
242    
243        public boolean isUseJpa() {
244            return useJpa;
245        }
246    
247        /**
248         * Sets whether we should use a JpaTraceEventMessage instead of
249         * an ordinary {@link org.apache.camel.processor.interceptor.DefaultTraceEventMessage}
250         * <p/>
251         * Use this to allow persistence of trace events into a database using JPA.
252         * This requires camel-jpa in the classpath.
253         */
254        public void setUseJpa(boolean useJpa) {
255            this.useJpa = useJpa;
256        }
257    
258        @Override
259        public String toString() {
260            return "Tracer";
261        }
262    
263        public void start() throws Exception {
264        }
265    
266        public void stop() throws Exception {
267        }
268    }