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 */
017package org.apache.camel.processor.interceptor;
018
019import java.util.List;
020import java.util.concurrent.CopyOnWriteArrayList;
021
022import org.apache.camel.CamelContext;
023import org.apache.camel.Endpoint;
024import org.apache.camel.LoggingLevel;
025import org.apache.camel.Predicate;
026import org.apache.camel.Processor;
027import org.apache.camel.Service;
028import org.apache.camel.model.ProcessorDefinition;
029import org.apache.camel.model.RouteDefinitionHelper;
030import org.apache.camel.processor.CamelLogProcessor;
031import org.apache.camel.spi.ExchangeFormatter;
032import org.apache.camel.spi.InterceptStrategy;
033import org.apache.camel.util.CamelLogger;
034
035/**
036 * An interceptor strategy for tracing routes
037 *
038 * @version 
039 */
040@Deprecated
041public class Tracer implements InterceptStrategy, Service {
042    private static final String JPA_TRACE_EVENT_MESSAGE = "org.apache.camel.processor.interceptor.jpa.JpaTraceEventMessage";
043
044    private TraceFormatter formatter = new DefaultTraceFormatter();
045    private boolean enabled = true;
046    private String logName = Tracer.class.getName();
047    private LoggingLevel logLevel = LoggingLevel.INFO;
048    private Predicate traceFilter;
049    private boolean traceInterceptors;
050    private boolean traceExceptions = true;
051    private boolean logStackTrace;
052    private boolean traceOutExchanges;
053    private String destinationUri;
054    private Endpoint destination;
055    private boolean useJpa;
056    private CamelLogProcessor logger;
057    private TraceInterceptorFactory traceInterceptorFactory = new DefaultTraceInterceptorFactory();
058    private final List<TraceEventHandler> traceHandlers = new CopyOnWriteArrayList<TraceEventHandler>();
059    private String jpaTraceEventMessageClassName = JPA_TRACE_EVENT_MESSAGE;
060    private boolean jmxTraceNotifications;
061    private int traceBodySize = 10000;
062    
063    public Tracer() {
064        traceHandlers.add(new DefaultTraceEventHandler(this));
065    }
066
067    /**
068     * Creates a new tracer.
069     *
070     * @param context Camel context
071     * @return a new tracer
072     */
073    public static Tracer createTracer(CamelContext context) {
074        Tracer tracer = new Tracer();
075        // lets see if we have a formatter if so use it
076        TraceFormatter formatter = context.getRegistry().lookupByNameAndType("traceFormatter", TraceFormatter.class);
077        if (formatter != null) {
078            tracer.setFormatter(formatter);
079        }
080        return tracer;
081    }
082
083    /**
084     * A helper method to return the Tracer instance if one is enabled
085     *
086     * @return the tracer or null if none can be found
087     */
088    public static Tracer getTracer(CamelContext context) {
089        List<InterceptStrategy> list = context.getInterceptStrategies();
090        for (InterceptStrategy interceptStrategy : list) {
091            if (interceptStrategy instanceof Tracer) {
092                return (Tracer) interceptStrategy;
093            }
094        }
095        return null;
096    }
097
098    /**
099     * Gets the logger to be used for tracers that can format and log a given exchange.
100     *
101     * @param formatter the exchange formatter
102     * @return the logger to use
103     */
104    public synchronized CamelLogProcessor getLogger(ExchangeFormatter formatter) {
105        if (logger == null) {
106            logger = new CamelLogProcessor(new CamelLogger(getLogName(), getLogLevel()), formatter, null, null);
107        }
108        return logger;
109    }
110
111    public Processor wrapProcessorInInterceptors(CamelContext context, ProcessorDefinition<?> definition,
112                                                 Processor target, Processor nextTarget) throws Exception {
113        // Force the creation of an id, otherwise the id is not available when the trace formatter is
114        // outputting trace information
115        RouteDefinitionHelper.forceAssignIds(context, definition);
116        return getTraceInterceptorFactory().createTraceInterceptor(definition, target, formatter, this);
117    }
118
119    public TraceFormatter getFormatter() {
120        return formatter;
121    }
122
123    public DefaultTraceFormatter getDefaultTraceFormatter() {
124        if (formatter instanceof DefaultTraceFormatter) {
125            return (DefaultTraceFormatter) formatter;
126        }
127        return null;
128    }
129
130    public void setFormatter(TraceFormatter formatter) {
131        this.formatter = formatter;
132    }
133
134    public void setEnabled(boolean flag) {
135        enabled = flag;
136    }
137
138    public boolean isEnabled() {
139        return enabled;
140    }
141
142    public boolean isTraceInterceptors() {
143        return traceInterceptors;
144    }
145
146    /**
147     * Sets whether interceptors should be traced or not
148     */
149    public void setTraceInterceptors(boolean traceInterceptors) {
150        this.traceInterceptors = traceInterceptors;
151    }
152
153    public Predicate getTraceFilter() {
154        return traceFilter;
155    }
156
157    /**
158     * Sets a predicate to be used as filter when tracing
159     */
160    public void setTraceFilter(Predicate traceFilter) {
161        this.traceFilter = traceFilter;
162    }
163
164    public LoggingLevel getLogLevel() {
165        return logLevel;
166    }
167
168    /**
169     * Sets the logging level to output tracing. Will use <tt>INFO</tt> level by default.
170     */
171    public void setLogLevel(LoggingLevel logLevel) {
172        this.logLevel = logLevel;
173        // update logger if its in use
174        if (logger != null) {
175            logger.getLogger().setLevel(logLevel);
176        }
177    }
178
179    public boolean isTraceExceptions() {
180        return traceExceptions;
181    }
182
183    /**
184     * Sets whether thrown exceptions should be traced
185     */
186    public void setTraceExceptions(boolean traceExceptions) {
187        this.traceExceptions = traceExceptions;
188    }
189
190    public boolean isLogStackTrace() {
191        return logStackTrace;
192    }
193
194    /**
195     * Sets whether thrown exception stacktrace should be traced, if disabled then only the exception message is logged
196     */
197    public void setLogStackTrace(boolean logStackTrace) {
198        this.logStackTrace = logStackTrace;
199    }
200
201    public String getLogName() {
202        return logName;
203    }
204
205    /**
206     * Sets the logging name to use.
207     * Will default use <tt>org.apache.camel.processor.interceptor.TraceInterceptor<tt>.
208     */
209    public void setLogName(String logName) {
210        this.logName = logName;
211        // update logger if its in use
212        if (logger != null) {
213            logger.getLogger().setLogName(logName);
214        }
215    }
216
217    /**
218     * Sets whether exchanges coming out of processors should be traced
219     */
220    public void setTraceOutExchanges(boolean traceOutExchanges) {
221        this.traceOutExchanges = traceOutExchanges;
222    }
223
224    public boolean isTraceOutExchanges() {
225        return traceOutExchanges;
226    }
227
228    public String getDestinationUri() {
229        return destinationUri;
230    }
231
232    /**
233     * Sets an optional destination to send the traced Exchange.
234     * <p/>
235     * Can be used to store tracing as files, in a database or whatever. The routing of the Exchange
236     * will happen synchronously and the original route will first continue when this destination routing
237     * has been completed.
238     */
239    public void setDestinationUri(String destinationUri) {
240        this.destinationUri = destinationUri;
241    }
242
243    public Endpoint getDestination() {
244        return destination;
245    }
246
247    /**
248     * See {@link #setDestinationUri(String)}
249     */
250    public void setDestination(Endpoint destination) {
251        this.destination = destination;
252    }
253
254    public boolean isUseJpa() {
255        return useJpa;
256    }
257
258    /**
259     * Sets whether we should use a JpaTraceEventMessage instead of
260     * an ordinary {@link org.apache.camel.processor.interceptor.DefaultTraceEventMessage}
261     * <p/>
262     * Use this to allow persistence of trace events into a database using JPA.
263     * This requires camel-jpa in the classpath.
264     */
265    public void setUseJpa(boolean useJpa) {
266        this.useJpa = useJpa;
267    }
268
269    public TraceInterceptorFactory getTraceInterceptorFactory() {
270        return this.traceInterceptorFactory;
271    }
272
273    /**
274     * Set the factory to be used to create the trace interceptor.
275     * It is expected that the factory will create a subclass of TraceInterceptor.
276     * <p/>
277     * Use this to take complete control of how trace events are handled.
278     * The TraceInterceptorFactory should only be set before any routes are created, hence this
279     * method is not thread safe.
280     */
281    public void setTraceInterceptorFactory(TraceInterceptorFactory traceInterceptorFactory) {
282        this.traceInterceptorFactory = traceInterceptorFactory;
283    }
284
285    /**
286     * 
287     * @return the first trace event handler
288     */
289    @Deprecated
290    public TraceEventHandler getTraceHandler() {
291        return traceHandlers.get(0);
292    }
293    
294    /**
295     * 
296     * @return list of tracehandlers
297     */
298    public List<TraceEventHandler> getTraceHandlers() {
299        return traceHandlers;
300    }
301
302    /**
303     * Set the object to be used to perform tracing.
304     * <p/>
305     * Use this to take more control of how trace events are persisted.
306     * Setting the traceHandler provides a simpler mechanism for controlling tracing
307     * than the TraceInterceptorFactory.
308     * The TraceHandler should only be set before any routes are created, hence this
309     * method is not thread safe.
310     */
311    @Deprecated
312    public void setTraceHandler(TraceEventHandler traceHandler) {
313        this.traceHandlers.clear();
314        this.traceHandlers.add(traceHandler);
315    }
316    
317    /**
318     * Add the given tracehandler
319     */
320    public void addTraceHandler(TraceEventHandler traceHandler) {
321        this.traceHandlers.add(traceHandler);
322    }
323    
324    /**
325     * Remove the given tracehandler
326     */
327    public void removeTraceHandler(TraceEventHandler traceHandler) {
328        this.traceHandlers.remove(traceHandler);
329    }
330
331    public String getJpaTraceEventMessageClassName() {
332        return jpaTraceEventMessageClassName;
333    }
334
335    /**
336     * Set the fully qualified name of the class to be used by the JPA event tracing.
337     * <p/>
338     * The class must exist in the classpath and be available for dynamic loading.
339     * The class name should only be set before any routes are created, hence this
340     * method is not thread safe.
341     */
342    public void setJpaTraceEventMessageClassName(String jpaTraceEventMessageClassName) {
343        this.jpaTraceEventMessageClassName = jpaTraceEventMessageClassName;
344    }
345
346    public boolean isJmxTraceNotifications() {
347        return jmxTraceNotifications;
348    }
349
350    public void setJmxTraceNotifications(boolean jmxTraceNotifications) {
351        this.jmxTraceNotifications = jmxTraceNotifications;
352    }
353
354    public int getTraceBodySize() {
355        return traceBodySize;
356    }
357
358    public void setTraceBodySize(int traceBodySize) {
359        this.traceBodySize = traceBodySize;
360    }
361
362    public void start() throws Exception {
363        // noop
364    }
365
366    public void stop() throws Exception {
367        traceHandlers.clear();
368    }
369
370    @Override
371    public String toString() {
372        return "Tracer";
373    }
374}