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 org.apache.camel.Exchange; 020import org.apache.camel.Message; 021import org.apache.camel.RouteNode; 022import org.apache.camel.model.ProcessorDefinition; 023import org.apache.camel.model.ProcessorDefinitionHelper; 024import org.apache.camel.model.RouteDefinition; 025import org.apache.camel.spi.TracedRouteNodes; 026import org.apache.camel.util.MessageHelper; 027 028/** 029 * @version 030 */ 031public class DefaultTraceFormatter implements TraceFormatter { 032 033 protected static final String LS = System.getProperty("line.separator"); 034 private static final String SEPARATOR = "###REPLACE_ME###"; 035 036 private int breadCrumbLength; 037 private int nodeLength; 038 private boolean showBreadCrumb = true; 039 private boolean showNode = true; 040 private boolean showExchangeId; 041 private boolean showShortExchangeId; 042 private boolean showExchangePattern = true; 043 private boolean showProperties; 044 private boolean showHeaders = true; 045 private boolean showBody = true; 046 private boolean showBodyType = true; 047 private boolean showOutHeaders; 048 private boolean showOutBody; 049 private boolean showOutBodyType; 050 private boolean showException = true; 051 private boolean showRouteId = true; 052 private boolean multiline; 053 054 private int maxChars = 10000; 055 056 public Object format(final TraceInterceptor interceptor, final ProcessorDefinition<?> node, final Exchange exchange) { 057 Message in = exchange.getIn(); 058 Message out = null; 059 if (exchange.hasOut()) { 060 out = exchange.getOut(); 061 } 062 063 StringBuilder sb = new StringBuilder(); 064 if (multiline) { 065 sb.append(SEPARATOR); 066 } 067 sb.append(extractBreadCrumb(interceptor, node, exchange)); 068 069 if (showExchangePattern) { 070 if (multiline) { 071 sb.append(SEPARATOR); 072 } 073 sb.append(", Pattern:").append(exchange.getPattern()); 074 } 075 // only show properties if we have any 076 if (showProperties && !exchange.getProperties().isEmpty()) { 077 if (multiline) { 078 sb.append(SEPARATOR); 079 } 080 sb.append(", Properties:").append(exchange.getProperties()); 081 } 082 // only show headers if we have any 083 if (showHeaders && !in.getHeaders().isEmpty()) { 084 if (multiline) { 085 sb.append(SEPARATOR); 086 } 087 sb.append(", Headers:").append(in.getHeaders()); 088 } 089 if (showBodyType) { 090 if (multiline) { 091 sb.append(SEPARATOR); 092 } 093 sb.append(", BodyType:").append(MessageHelper.getBodyTypeName(in)); 094 } 095 if (showBody) { 096 if (multiline) { 097 sb.append(SEPARATOR); 098 } 099 sb.append(", Body:").append(MessageHelper.extractBodyForLogging(in, "")); 100 } 101 if (showOutHeaders && out != null) { 102 if (multiline) { 103 sb.append(SEPARATOR); 104 } 105 sb.append(", OutHeaders:").append(out.getHeaders()); 106 } 107 if (showOutBodyType && out != null) { 108 if (multiline) { 109 sb.append(SEPARATOR); 110 } 111 sb.append(", OutBodyType:").append(MessageHelper.getBodyTypeName(out)); 112 } 113 if (showOutBody && out != null) { 114 if (multiline) { 115 sb.append(SEPARATOR); 116 } 117 sb.append(", OutBody:").append(MessageHelper.extractBodyForLogging(out, "")); 118 } 119 if (showException && exchange.getException() != null) { 120 if (multiline) { 121 sb.append(SEPARATOR); 122 } 123 sb.append(", Exception:").append(exchange.getException()); 124 } 125 126 // replace ugly <<<, with <<< 127 sb = new StringBuilder(sb.toString().replaceFirst("<<<,", "<<<")); 128 129 if (maxChars > 0) { 130 StringBuilder answer = new StringBuilder(); 131 for (String s : sb.toString().split(SEPARATOR)) { 132 if (s != null) { 133 if (s.length() > maxChars) { 134 s = s.substring(0, maxChars); 135 answer.append(s).append("..."); 136 } else { 137 answer.append(s); 138 } 139 if (multiline) { 140 answer.append(LS); 141 } 142 } 143 } 144 145 // switch string buffer 146 sb = answer; 147 } 148 149 return sb.toString(); 150 } 151 152 public boolean isShowBody() { 153 return showBody; 154 } 155 156 public void setShowBody(boolean showBody) { 157 this.showBody = showBody; 158 } 159 160 public boolean isShowBodyType() { 161 return showBodyType; 162 } 163 164 public void setShowBodyType(boolean showBodyType) { 165 this.showBodyType = showBodyType; 166 } 167 168 public void setShowOutBody(boolean showOutBody) { 169 this.showOutBody = showOutBody; 170 } 171 172 public boolean isShowOutBody() { 173 return showOutBody; 174 } 175 176 public void setShowOutBodyType(boolean showOutBodyType) { 177 this.showOutBodyType = showOutBodyType; 178 } 179 180 public boolean isShowOutBodyType() { 181 return showOutBodyType; 182 } 183 184 public boolean isShowBreadCrumb() { 185 return showBreadCrumb; 186 } 187 188 public void setShowBreadCrumb(boolean showBreadCrumb) { 189 this.showBreadCrumb = showBreadCrumb; 190 } 191 192 public boolean isShowExchangeId() { 193 return showExchangeId; 194 } 195 196 public void setShowExchangeId(boolean showExchangeId) { 197 this.showExchangeId = showExchangeId; 198 } 199 200 public boolean isShowHeaders() { 201 return showHeaders; 202 } 203 204 public void setShowHeaders(boolean showHeaders) { 205 this.showHeaders = showHeaders; 206 } 207 208 public boolean isShowOutHeaders() { 209 return showOutHeaders; 210 } 211 212 public void setShowOutHeaders(boolean showOutHeaders) { 213 this.showOutHeaders = showOutHeaders; 214 } 215 216 public boolean isShowProperties() { 217 return showProperties; 218 } 219 220 public void setShowProperties(boolean showProperties) { 221 this.showProperties = showProperties; 222 } 223 224 public boolean isShowNode() { 225 return showNode; 226 } 227 228 public void setShowNode(boolean showNode) { 229 this.showNode = showNode; 230 } 231 232 public boolean isShowExchangePattern() { 233 return showExchangePattern; 234 } 235 236 public void setShowExchangePattern(boolean showExchangePattern) { 237 this.showExchangePattern = showExchangePattern; 238 } 239 240 public boolean isShowException() { 241 return showException; 242 } 243 244 public void setShowException(boolean showException) { 245 this.showException = showException; 246 } 247 248 public boolean isShowRouteId() { 249 return showRouteId; 250 } 251 252 public void setShowRouteId(boolean showRouteId) { 253 this.showRouteId = showRouteId; 254 } 255 256 public boolean isMultiline() { 257 return multiline; 258 } 259 260 public void setMultiline(boolean multiline) { 261 this.multiline = multiline; 262 } 263 264 public int getBreadCrumbLength() { 265 return breadCrumbLength; 266 } 267 268 public void setBreadCrumbLength(int breadCrumbLength) { 269 this.breadCrumbLength = breadCrumbLength; 270 } 271 272 public boolean isShowShortExchangeId() { 273 return showShortExchangeId; 274 } 275 276 public void setShowShortExchangeId(boolean showShortExchangeId) { 277 this.showShortExchangeId = showShortExchangeId; 278 } 279 280 public int getNodeLength() { 281 return nodeLength; 282 } 283 284 public void setNodeLength(int nodeLength) { 285 this.nodeLength = nodeLength; 286 } 287 288 public int getMaxChars() { 289 return maxChars; 290 } 291 292 public void setMaxChars(int maxChars) { 293 this.maxChars = maxChars; 294 } 295 296 // Implementation methods 297 //------------------------------------------------------------------------- 298 299 protected String extractRoute(ProcessorDefinition<?> node) { 300 RouteDefinition route = ProcessorDefinitionHelper.getRoute(node); 301 if (route != null) { 302 return route.getId(); 303 } else { 304 return null; 305 } 306 } 307 308 protected Object getBreadCrumbID(Exchange exchange) { 309 return exchange.getExchangeId(); 310 } 311 312 protected String getNodeMessage(RouteNode entry, Exchange exchange) { 313 String message = entry.getLabel(exchange); 314 if (nodeLength > 0) { 315 return String.format("%1$-" + nodeLength + "." + nodeLength + "s", message); 316 } else { 317 return message; 318 } 319 } 320 321 /** 322 * Creates the breadcrumb based on whether this was a trace of 323 * an exchange coming out of or into a processing step. For example, 324 * <br/><tt>transform(body) -> ID-mojo/39713-1225468755256/2-0</tt> 325 * <br/>or 326 * <br/><tt>ID-mojo/39713-1225468755256/2-0 -> transform(body)</tt> 327 */ 328 protected String extractBreadCrumb(TraceInterceptor interceptor, ProcessorDefinition<?> currentNode, Exchange exchange) { 329 String id = ""; 330 String result; 331 332 if (!showBreadCrumb && !showExchangeId && !showShortExchangeId && !showNode) { 333 return ""; 334 } 335 336 // compute breadcrumb id 337 if (showBreadCrumb) { 338 id = getBreadCrumbID(exchange).toString(); 339 } else if (showExchangeId || showShortExchangeId) { 340 id = getBreadCrumbID(exchange).toString(); 341 if (showShortExchangeId) { 342 // only output last part of id 343 id = id.substring(id.lastIndexOf('-') + 1); 344 } 345 } 346 347 // compute from, to and route 348 String from = ""; 349 String to = ""; 350 String route = ""; 351 if (showNode || showRouteId) { 352 if (exchange.getUnitOfWork() != null) { 353 TracedRouteNodes traced = exchange.getUnitOfWork().getTracedRouteNodes(); 354 355 RouteNode traceFrom = traced.getSecondLastNode(); 356 if (traceFrom != null) { 357 from = getNodeMessage(traceFrom, exchange); 358 } else if (exchange.getFromEndpoint() != null) { 359 from = "from(" + exchange.getFromEndpoint().getEndpointUri() + ")"; 360 } 361 362 RouteNode traceTo = traced.getLastNode(); 363 if (traceTo != null) { 364 to = getNodeMessage(traceTo, exchange); 365 // if its an abstract dummy holder then we have to get the 2nd last so we can get the real node that has 366 // information which route it belongs to 367 if (traceTo.isAbstract() && traceTo.getProcessorDefinition() == null) { 368 traceTo = traced.getSecondLastNode(); 369 } 370 if (traceTo != null) { 371 route = extractRoute(traceTo.getProcessorDefinition()); 372 } 373 } 374 } 375 } 376 377 // assemble result with and without the to/from 378 if (showNode) { 379 if (showRouteId && route != null) { 380 result = id.trim() + " >>> (" + route + ") " + from + " --> " + to.trim() + " <<< "; 381 } else { 382 result = id.trim() + " >>> " + from + " --> " + to.trim() + " <<< "; 383 } 384 385 if (interceptor.shouldTraceOutExchanges() && exchange.hasOut()) { 386 result += " (OUT) "; 387 } 388 } else { 389 result = id; 390 } 391 392 if (breadCrumbLength > 0) { 393 // we want to ensure text coming after this is aligned for readability 394 return String.format("%1$-" + breadCrumbLength + "." + breadCrumbLength + "s", result.trim()); 395 } else { 396 return result.trim(); 397 } 398 } 399 400}