001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.html;
015
016import java.util.Map;
017
018import ch.qos.logback.classic.PatternLayout;
019import ch.qos.logback.classic.pattern.MDCConverter;
020import ch.qos.logback.classic.spi.ILoggingEvent;
021import ch.qos.logback.classic.html.DefaultCssBuilder;
022import ch.qos.logback.core.helpers.Transform;
023import ch.qos.logback.core.html.HTMLLayoutBase;
024import ch.qos.logback.core.html.IThrowableRenderer;
025import ch.qos.logback.core.pattern.Converter;
026import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;
027
028/**
029 * 
030 * HTMLLayout outputs events in an HTML table. <p> The content of the table
031 * columns are specified using a conversion pattern. See
032 * {@link ch.qos.logback.classic.PatternLayout} for documentation on the
033 * available patterns. <p> For more information about this layout, please refer
034 * to the online manual at
035 * http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout
036 * 
037 * @author Ceki G&uuml;lc&uuml;
038 * @author S&eacute;bastien Pennec
039 */
040public class HTMLLayout extends HTMLLayoutBase<ILoggingEvent> {
041
042    /**
043     * Default pattern string for log output.
044     */
045    static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg";
046
047    IThrowableRenderer<ILoggingEvent> throwableRenderer;
048
049    /**
050     * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
051     * 
052     * The default pattern just produces the application supplied message.
053     */
054    public HTMLLayout() {
055        pattern = DEFAULT_CONVERSION_PATTERN;
056        throwableRenderer = new DefaultThrowableRenderer();
057        cssBuilder = new DefaultCssBuilder();
058    }
059
060    @Override
061    public void start() {
062        int errorCount = 0;
063        if (throwableRenderer == null) {
064            addError("ThrowableRender cannot be null.");
065            errorCount++;
066        }
067        if (errorCount == 0) {
068            super.start();
069        }
070    }
071
072    protected Map<String, String> getDefaultConverterMap() {
073        return PatternLayout.defaultConverterMap;
074    }
075
076    public String doLayout(ILoggingEvent event) {
077        StringBuilder buf = new StringBuilder();
078        startNewTableIfLimitReached(buf);
079
080        boolean odd = true;
081        if (((counter++) & 1) == 0) {
082            odd = false;
083        }
084
085        String level = event.getLevel().toString().toLowerCase();
086
087        buf.append(LINE_SEPARATOR);
088        buf.append("<tr class=\"");
089        buf.append(level);
090        if (odd) {
091            buf.append(" odd\">");
092        } else {
093            buf.append(" even\">");
094        }
095        buf.append(LINE_SEPARATOR);
096
097        Converter<ILoggingEvent> c = head;
098        while (c != null) {
099            appendEventToBuffer(buf, c, event);
100            c = c.getNext();
101        }
102        buf.append("</tr>");
103        buf.append(LINE_SEPARATOR);
104
105        if (event.getThrowableProxy() != null) {
106            throwableRenderer.render(buf, event);
107        }
108        return buf.toString();
109    }
110
111    private void appendEventToBuffer(StringBuilder buf, Converter<ILoggingEvent> c, ILoggingEvent event) {
112        buf.append("<td class=\"");
113        buf.append(computeConverterName(c));
114        buf.append("\">");
115        buf.append(Transform.escapeTags(c.convert(event)));
116        buf.append("</td>");
117        buf.append(LINE_SEPARATOR);
118    }
119
120    public IThrowableRenderer<ILoggingEvent> getThrowableRenderer() {
121        return throwableRenderer;
122    }
123
124    public void setThrowableRenderer(IThrowableRenderer<ILoggingEvent> throwableRenderer) {
125        this.throwableRenderer = throwableRenderer;
126    }
127
128    @Override
129    protected String computeConverterName(Converter<ILoggingEvent> c) {
130        if (c instanceof MDCConverter) {
131            MDCConverter mc = (MDCConverter) c;
132            String key = mc.getFirstOption();
133            if (key != null) {
134                return key;
135            } else {
136                return "MDC";
137            }
138        } else {
139            return super.computeConverterName(c);
140        }
141    }
142}