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;
018
019import java.io.PrintWriter;
020import java.io.StringWriter;
021import java.util.Map;
022import java.util.TreeMap;
023import java.util.concurrent.Future;
024
025import org.apache.camel.Exchange;
026import org.apache.camel.Message;
027import org.apache.camel.spi.ExchangeFormatter;
028import org.apache.camel.util.MessageHelper;
029import org.apache.camel.util.ObjectHelper;
030import org.apache.camel.util.StringHelper;
031
032/**
033 * Default {@link ExchangeFormatter} that have fine grained options to configure what to include in the output.
034 */
035public class DefaultExchangeFormatter implements ExchangeFormatter {
036
037    protected static final String LS = System.getProperty("line.separator");
038    private static final String SEPARATOR = "###REPLACE_ME###";
039
040    public enum OutputStyle { Default, Tab, Fixed }
041
042    private boolean showExchangeId;
043    private boolean showExchangePattern = true;
044    private boolean showProperties;
045    private boolean showHeaders;
046    private boolean skipBodyLineSeparator = true;
047    private boolean showBodyType = true;
048    private boolean showBody = true;
049    private boolean showOut;
050    private boolean showException;
051    private boolean showCaughtException;
052    private boolean showStackTrace;
053    private boolean showAll;
054    private boolean multiline;
055    private boolean showFuture;
056    private boolean showStreams;
057    private boolean showFiles;
058    private int maxChars = 10000;
059    private OutputStyle style = OutputStyle.Default;
060
061    private String style(String label) {
062        if (style == OutputStyle.Default) {
063            return String.format(", %s: ", label);
064        } 
065        if (style == OutputStyle.Tab) {
066            return String.format("\t%s: ", label);
067        } else {
068            return String.format("\t%-20s", label);
069        }
070    }
071
072    public String format(Exchange exchange) {
073        Message in = exchange.getIn();
074
075        StringBuilder sb = new StringBuilder();
076        if (showAll || showExchangeId) {
077            if (multiline) {
078                sb.append(SEPARATOR);
079            }
080            sb.append(style("Id")).append(exchange.getExchangeId());
081        }
082        if (showAll || showExchangePattern) {
083            if (multiline) {
084                sb.append(SEPARATOR);
085            }
086            sb.append(style("ExchangePattern")).append(exchange.getPattern());
087        }
088
089        if (showAll || showProperties) {
090            if (multiline) {
091                sb.append(SEPARATOR);
092            }
093            sb.append(style("Properties")).append(sortMap(exchange.getProperties()));
094        }
095        if (showAll || showHeaders) {
096            if (multiline) {
097                sb.append(SEPARATOR);
098            }
099            sb.append(style("Headers")).append(sortMap(in.getHeaders()));
100        }
101        if (showAll || showBodyType) {
102            if (multiline) {
103                sb.append(SEPARATOR);
104            }
105            sb.append(style("BodyType")).append(getBodyTypeAsString(in));
106        }
107        if (showAll || showBody) {
108            if (multiline) {
109                sb.append(SEPARATOR);
110            }
111            String body = getBodyAsString(in);
112            if (skipBodyLineSeparator) {
113                body = StringHelper.replaceAll(body, LS, "");
114            }
115            sb.append(style("Body")).append(body);
116        }
117
118        if (showAll || showException || showCaughtException) {
119
120            // try exception on exchange first
121            Exception exception = exchange.getException();
122            boolean caught = false;
123            if ((showAll || showCaughtException) && exception == null) {
124                // fallback to caught exception
125                exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
126                caught = true;
127            }
128
129            if (exception != null) {
130                if (multiline) {
131                    sb.append(SEPARATOR);
132                }
133                if (caught) {
134                    sb.append(style("CaughtExceptionType")).append(exception.getClass().getCanonicalName());
135                    sb.append(style("CaughtExceptionMessage")).append(exception.getMessage());
136                } else {
137                    sb.append(style("ExceptionType")).append(exception.getClass().getCanonicalName());
138                    sb.append(style("ExceptionMessage")).append(exception.getMessage());
139                }
140                if (showAll || showStackTrace) {
141                    StringWriter sw = new StringWriter();
142                    exception.printStackTrace(new PrintWriter(sw));
143                    sb.append(style("StackTrace")).append(sw.toString());
144                }
145            }
146        }
147
148        if (showAll || showOut) {
149            if (exchange.hasOut()) {
150                Message out = exchange.getOut();
151                if (showAll || showHeaders) {
152                    if (multiline) {
153                        sb.append(SEPARATOR);
154                    }
155                    sb.append(style("OutHeaders")).append(sortMap(out.getHeaders()));
156                }
157                if (showAll || showBodyType) {
158                    if (multiline) {
159                        sb.append(SEPARATOR);
160                    }
161                    sb.append(style("OutBodyType")).append(getBodyTypeAsString(out));
162                }
163                if (showAll || showBody) {
164                    if (multiline) {
165                        sb.append(SEPARATOR);
166                    }
167                    String body = getBodyAsString(out);
168                    if (skipBodyLineSeparator) {
169                        body = StringHelper.replaceAll(body, LS, "");
170                    }
171                    sb.append(style("OutBody")).append(body);
172                }
173            } else {
174                if (multiline) {
175                    sb.append(SEPARATOR);
176                }
177                sb.append(style("Out: null"));
178            }
179        }
180
181        if (maxChars > 0) {
182            StringBuilder answer = new StringBuilder();
183            for (String s : sb.toString().split(SEPARATOR)) {
184                if (s != null) {
185                    if (s.length() > maxChars) {
186                        s = s.substring(0, maxChars);
187                        answer.append(s).append("...");
188                    } else {
189                        answer.append(s);
190                    }
191                    if (multiline) {
192                        answer.append(LS);
193                    }
194                }
195            }
196
197            // switch string buffer
198            sb = answer;
199        }
200
201        if (multiline) {
202            sb.insert(0, "Exchange[");
203            sb.append("]");
204            return sb.toString();
205        } else {
206            // get rid of the leading space comma if needed
207            if (sb.length() > 0 && sb.charAt(0) == ',' && sb.charAt(1) == ' ') {
208                sb.replace(0, 2, "");
209            }
210            sb.insert(0, "Exchange[");
211            sb.append("]");
212
213            return sb.toString();
214        }
215    }
216
217    public boolean isShowExchangeId() {
218        return showExchangeId;
219    }
220
221    public void setShowExchangeId(boolean showExchangeId) {
222        this.showExchangeId = showExchangeId;
223    }
224
225    public boolean isShowProperties() {
226        return showProperties;
227    }
228
229    public void setShowProperties(boolean showProperties) {
230        this.showProperties = showProperties;
231    }
232
233    public boolean isShowHeaders() {
234        return showHeaders;
235    }
236
237    public void setShowHeaders(boolean showHeaders) {
238        this.showHeaders = showHeaders;
239    }
240
241    public boolean isSkipBodyLineSeparator() {
242        return skipBodyLineSeparator;
243    }
244
245    public void setSkipBodyLineSeparator(boolean skipBodyLineSeparator) {
246        this.skipBodyLineSeparator = skipBodyLineSeparator;
247    }
248
249    public boolean isShowBodyType() {
250        return showBodyType;
251    }
252
253    public void setShowBodyType(boolean showBodyType) {
254        this.showBodyType = showBodyType;
255    }
256
257    public boolean isShowBody() {
258        return showBody;
259    }
260
261    public void setShowBody(boolean showBody) {
262        this.showBody = showBody;
263    }
264
265    public boolean isShowOut() {
266        return showOut;
267    }
268
269    public void setShowOut(boolean showOut) {
270        this.showOut = showOut;
271    }
272
273    public boolean isShowAll() {
274        return showAll;
275    }
276
277    public void setShowAll(boolean showAll) {
278        this.showAll = showAll;
279    }
280
281    public boolean isShowException() {
282        return showException;
283    }
284
285    public void setShowException(boolean showException) {
286        this.showException = showException;
287    }
288
289    public boolean isShowStackTrace() {
290        return showStackTrace;
291    }
292
293    public void setShowStackTrace(boolean showStackTrace) {
294        this.showStackTrace = showStackTrace;
295    }
296
297    public boolean isShowCaughtException() {
298        return showCaughtException;
299    }
300
301    public void setShowCaughtException(boolean showCaughtException) {
302        this.showCaughtException = showCaughtException;
303    }
304
305    public boolean isMultiline() {
306        return multiline;
307    }
308
309    public int getMaxChars() {
310        return maxChars;
311    }
312
313    public void setMaxChars(int maxChars) {
314        this.maxChars = maxChars;
315    }
316
317    /**
318     * If enabled then each information is outputted on a newline.
319     */
320    public void setMultiline(boolean multiline) {
321        this.multiline = multiline;
322    }
323
324    public boolean isShowFuture() {
325        return showFuture;
326    }
327
328    /**
329     * If enabled Camel will on Future objects wait for it to complete to obtain the payload to be logged.
330     * <p/>
331     * Is default disabled.
332     */
333    public void setShowFuture(boolean showFuture) {
334        this.showFuture = showFuture;
335    }
336
337    public boolean isShowExchangePattern() {
338        return showExchangePattern;
339    }
340
341    public void setShowExchangePattern(boolean showExchangePattern) {
342        this.showExchangePattern = showExchangePattern;
343    }
344
345    public boolean isShowStreams() {
346        return showStreams;
347    }
348
349    /**
350     * If enabled Camel will output stream objects
351     * <p/>
352     * Is default disabled.
353     */
354    public void setShowStreams(boolean showStreams) {
355        this.showStreams = showStreams;
356    }
357
358    public boolean isShowFiles() {
359        return showFiles;
360    }
361
362    /**
363     * If enabled Camel will output files
364     * <p/>
365     * Is default disabled.
366     */
367    public void setShowFiles(boolean showFiles) {
368        this.showFiles = showFiles;
369    }
370
371    public OutputStyle getStyle() {
372        return style;
373    }
374
375    /**
376     * Sets the outputs style to use.
377     */
378    public void setStyle(OutputStyle style) {
379        this.style = style;
380    }
381
382    // Implementation methods
383    //-------------------------------------------------------------------------
384    protected String getBodyAsString(Message message) {
385        if (message.getBody() instanceof Future) {
386            if (!isShowFuture()) {
387                // just use a to string of the future object
388                return message.getBody().toString();
389            }
390        }
391
392        return MessageHelper.extractBodyForLogging(message, "", isShowStreams(), isShowFiles(), getMaxChars(message));
393    }
394
395    private int getMaxChars(Message message) {
396        int maxChars = getMaxChars();
397        if (message.getExchange() != null) {
398            String property = message.getExchange().getContext().getProperty(Exchange.LOG_DEBUG_BODY_MAX_CHARS);
399            if (property != null) {
400                maxChars = message.getExchange().getContext().getTypeConverter().convertTo(Integer.class, property);
401            }
402        }
403        return maxChars;
404    }
405
406    protected String getBodyTypeAsString(Message message) {
407        String answer = ObjectHelper.classCanonicalName(message.getBody());
408        if (answer != null && answer.startsWith("java.lang.")) {
409            return answer.substring(10);
410        }
411        return answer;
412    }
413
414    private static Map<String, Object> sortMap(Map<String, Object> map) {
415        Map<String, Object> answer = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
416        answer.putAll(map);
417        return answer;
418    }
419
420}