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}