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    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}