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