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.core.helpers.Transform;
022import ch.qos.logback.core.html.HTMLLayoutBase;
023import ch.qos.logback.core.html.IThrowableRenderer;
024import ch.qos.logback.core.pattern.Converter;
025import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;
026
027/**
028 * 
029 * HTMLLayout outputs events in an HTML table.
030 * <p>
031 * The content of the table columns are specified using a conversion pattern.
032 * See {@link ch.qos.logback.classic.PatternLayout} for documentation on the
033 * available patterns.
034 * <p>
035 * For more information about this layout, please refer to the online manual at
036 * http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout
037 * 
038 * @author Ceki G&uuml;lc&uuml;
039 * @author S&eacute;bastien Pennec
040 */
041public class HTMLLayout extends HTMLLayoutBase<ILoggingEvent> {
042
043    /**
044     * Default pattern string for log output.
045     */
046    static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg";
047
048    IThrowableRenderer<ILoggingEvent> throwableRenderer;
049
050    /**
051     * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
052     * 
053     * The default pattern just produces the application supplied message.
054     */
055    public HTMLLayout() {
056        pattern = DEFAULT_CONVERSION_PATTERN;
057        throwableRenderer = new DefaultThrowableRenderer();
058        cssBuilder = new DefaultCssBuilder();
059    }
060
061    @Override
062    public void start() {
063        int errorCount = 0;
064        if (throwableRenderer == null) {
065            addError("ThrowableRender cannot be null.");
066            errorCount++;
067        }
068        if (errorCount == 0) {
069            super.start();
070        }
071    }
072
073    protected Map<String, String> getDefaultConverterMap() {
074        return PatternLayout.DEFAULT_CONVERTER_MAP;
075    }
076
077    public String doLayout(ILoggingEvent event) {
078        StringBuilder buf = new StringBuilder();
079        startNewTableIfLimitReached(buf);
080
081        boolean odd = true;
082        if (((counter++) & 1) == 0) {
083            odd = false;
084        }
085
086        String level = event.getLevel().toString().toLowerCase();
087
088        buf.append(LINE_SEPARATOR);
089        buf.append("<tr class=\"");
090        buf.append(level);
091        if (odd) {
092            buf.append(" odd\">");
093        } else {
094            buf.append(" even\">");
095        }
096        buf.append(LINE_SEPARATOR);
097
098        Converter<ILoggingEvent> c = head;
099        while (c != null) {
100            appendEventToBuffer(buf, c, event);
101            c = c.getNext();
102        }
103        buf.append("</tr>");
104        buf.append(LINE_SEPARATOR);
105
106        if (event.getThrowableProxy() != null) {
107            throwableRenderer.render(buf, event);
108        }
109        return buf.toString();
110    }
111
112    private void appendEventToBuffer(StringBuilder buf, Converter<ILoggingEvent> c, ILoggingEvent event) {
113        buf.append("<td class=\"");
114        buf.append(computeConverterName(c));
115        buf.append("\">");
116        buf.append(Transform.escapeTags(c.convert(event)));
117        buf.append("</td>");
118        buf.append(LINE_SEPARATOR);
119    }
120
121    public IThrowableRenderer<ILoggingEvent> getThrowableRenderer() {
122        return throwableRenderer;
123    }
124
125    public void setThrowableRenderer(IThrowableRenderer<ILoggingEvent> throwableRenderer) {
126        this.throwableRenderer = throwableRenderer;
127    }
128
129    @Override
130    protected String computeConverterName(Converter<ILoggingEvent> c) {
131        if (c instanceof MDCConverter) {
132            MDCConverter mc = (MDCConverter) c;
133            String key = mc.getFirstOption();
134            if (key != null) {
135                return key;
136            } else {
137                return "MDC";
138            }
139        } else {
140            return super.computeConverterName(c);
141        }
142    }
143}