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