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