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    }