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().lookupByNameAndType("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 }