1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.html;
15  
16  import java.util.Map;
17  import java.util.function.Supplier;
18  
19  import ch.qos.logback.classic.PatternLayout;
20  import ch.qos.logback.classic.pattern.MDCConverter;
21  import ch.qos.logback.classic.spi.ILoggingEvent;
22  import ch.qos.logback.core.helpers.Transform;
23  import ch.qos.logback.core.html.HTMLLayoutBase;
24  import ch.qos.logback.core.html.IThrowableRenderer;
25  import ch.qos.logback.core.pattern.Converter;
26  import ch.qos.logback.core.pattern.DynamicConverter;
27  
28  import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;
29  
30  /**
31   * 
32   * HTMLLayout outputs events in an HTML table.
33   * <p>
34   * The content of the table columns are specified using a conversion pattern.
35   * See {@link ch.qos.logback.classic.PatternLayout} for documentation on the
36   * available patterns.
37   * <p>
38   * For more information about this layout, please refer to the online manual at
39   * http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout
40   * 
41   * @author Ceki G&uuml;lc&uuml;
42   * @author S&eacute;bastien Pennec
43   */
44  public class HTMLLayout extends HTMLLayoutBase<ILoggingEvent> {
45  
46      /**
47       * Default pattern string for log output.
48       */
49      static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg";
50  
51      IThrowableRenderer<ILoggingEvent> throwableRenderer;
52  
53      /**
54       * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
55       * 
56       * The default pattern just produces the application supplied message.
57       */
58      public HTMLLayout() {
59          pattern = DEFAULT_CONVERSION_PATTERN;
60          throwableRenderer = new DefaultThrowableRenderer();
61          cssBuilder = new DefaultCssBuilder();
62      }
63  
64      @Override
65      public void start() {
66          int errorCount = 0;
67          if (throwableRenderer == null) {
68              addError("ThrowableRender cannot be null.");
69              errorCount++;
70          }
71          if (errorCount == 0) {
72              super.start();
73          }
74      }
75  
76      @Override
77      protected Map<String, Supplier<DynamicConverter>>  getDefaultConverterSupplierMap() {
78          return PatternLayout.DEFAULT_CONVERTER_SUPPLIER_MAP;
79      }
80  
81      public String doLayout(ILoggingEvent event) {
82          StringBuilder buf = new StringBuilder();
83          startNewTableIfLimitReached(buf);
84  
85          boolean odd = true;
86          if (((counter++) & 1) == 0) {
87              odd = false;
88          }
89  
90          String level = event.getLevel().toString().toLowerCase();
91  
92          buf.append(LINE_SEPARATOR);
93          buf.append("<tr class=\"");
94          buf.append(level);
95          if (odd) {
96              buf.append(" odd\">");
97          } else {
98              buf.append(" even\">");
99          }
100         buf.append(LINE_SEPARATOR);
101 
102         Converter<ILoggingEvent> c = head;
103         while (c != null) {
104             appendEventToBuffer(buf, c, event);
105             c = c.getNext();
106         }
107         buf.append("</tr>");
108         buf.append(LINE_SEPARATOR);
109 
110         if (event.getThrowableProxy() != null) {
111             throwableRenderer.render(buf, event);
112         }
113         return buf.toString();
114     }
115 
116     private void appendEventToBuffer(StringBuilder buf, Converter<ILoggingEvent> c, ILoggingEvent event) {
117         buf.append("<td class=\"");
118         buf.append(computeConverterName(c));
119         buf.append("\">");
120         buf.append(Transform.escapeTags(c.convert(event)));
121         buf.append("</td>");
122         buf.append(LINE_SEPARATOR);
123     }
124 
125     public IThrowableRenderer<ILoggingEvent> getThrowableRenderer() {
126         return throwableRenderer;
127     }
128 
129     public void setThrowableRenderer(IThrowableRenderer<ILoggingEvent> throwableRenderer) {
130         this.throwableRenderer = throwableRenderer;
131     }
132 
133     @Override
134     protected String computeConverterName(Converter<ILoggingEvent> c) {
135         if (c instanceof MDCConverter) {
136             MDCConverter mc = (MDCConverter) c;
137             String key = mc.getFirstOption();
138             if (key != null) {
139                 return key;
140             } else {
141                 return "MDC";
142             }
143         } else {
144             return super.computeConverterName(c);
145         }
146     }
147 }