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;
018    
019    import java.io.PrintWriter;
020    import java.io.StringWriter;
021    import java.util.Map;
022    import java.util.TreeMap;
023    import java.util.concurrent.Future;
024    
025    import org.apache.camel.Exchange;
026    import org.apache.camel.Message;
027    import org.apache.camel.spi.ExchangeFormatter;
028    import org.apache.camel.util.MessageHelper;
029    import org.apache.camel.util.ObjectHelper;
030    import 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     */
035    public 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());
393        }
394    
395        protected String getBodyTypeAsString(Message message) {
396            String answer = ObjectHelper.classCanonicalName(message.getBody());
397            if (answer != null && answer.startsWith("java.lang.")) {
398                return answer.substring(10);
399            }
400            return answer;
401        }
402    
403        private static Map<String, Object> sortMap(Map<String, Object> map) {
404            Map<String, Object> answer = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
405            answer.putAll(map);
406            return answer;
407        }
408    
409    }