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ülcü 039 * @author Sé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}