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;
017import java.util.function.Supplier;
018
019import ch.qos.logback.classic.PatternLayout;
020import ch.qos.logback.classic.pattern.MDCConverter;
021import ch.qos.logback.classic.spi.ILoggingEvent;
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 ch.qos.logback.core.pattern.DynamicConverter;
027
028import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;
029
030/**
031 * 
032 * HTMLLayout outputs events in an HTML table.
033 * <p>
034 * The content of the table columns are specified using a conversion pattern.
035 * See {@link ch.qos.logback.classic.PatternLayout} for documentation on the
036 * available patterns.
037 * <p>
038 * For more information about this layout, please refer to the online manual at
039 * http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout
040 * 
041 * @author Ceki G&uuml;lc&uuml;
042 * @author S&eacute;bastien Pennec
043 */
044public class HTMLLayout extends HTMLLayoutBase<ILoggingEvent> {
045
046    /**
047     * Default pattern string for log output.
048     */
049    static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg";
050
051    IThrowableRenderer<ILoggingEvent> throwableRenderer;
052
053    /**
054     * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
055     * 
056     * The default pattern just produces the application supplied message.
057     */
058    public HTMLLayout() {
059        pattern = DEFAULT_CONVERSION_PATTERN;
060        throwableRenderer = new DefaultThrowableRenderer();
061        cssBuilder = new DefaultCssBuilder();
062    }
063
064    @Override
065    public void start() {
066        int errorCount = 0;
067        if (throwableRenderer == null) {
068            addError("ThrowableRender cannot be null.");
069            errorCount++;
070        }
071        if (errorCount == 0) {
072            super.start();
073        }
074    }
075
076    @Override
077    protected Map<String, Supplier<DynamicConverter>>  getDefaultConverterSupplierMap() {
078        return PatternLayout.DEFAULT_CONVERTER_SUPPLIER_MAP;
079    }
080
081    public String doLayout(ILoggingEvent event) {
082        StringBuilder buf = new StringBuilder();
083        startNewTableIfLimitReached(buf);
084
085        boolean odd = true;
086        if (((counter++) & 1) == 0) {
087            odd = false;
088        }
089
090        String level = event.getLevel().toString().toLowerCase();
091
092        buf.append(LINE_SEPARATOR);
093        buf.append("<tr class=\"");
094        buf.append(level);
095        if (odd) {
096            buf.append(" odd\">");
097        } else {
098            buf.append(" even\">");
099        }
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}