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.core.html; 015 016import java.util.HashMap; 017import java.util.Map; 018import java.util.function.Supplier; 019 020import ch.qos.logback.core.Context; 021import ch.qos.logback.core.CoreConstants; 022import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; 023import ch.qos.logback.core.LayoutBase; 024import ch.qos.logback.core.pattern.Converter; 025import ch.qos.logback.core.pattern.ConverterUtil; 026import ch.qos.logback.core.pattern.DynamicConverter; 027import ch.qos.logback.core.pattern.parser.Node; 028import ch.qos.logback.core.pattern.parser.Parser; 029import ch.qos.logback.core.spi.ScanException; 030 031/** 032 * This class is a base class for HTMLLayout classes part of other logback 033 * modules such as logback-classic and logback-access. 034 * 035 * 036 * @author Sébastien Pennec 037 */ 038public abstract class HTMLLayoutBase<E> extends LayoutBase<E> { 039 040 protected String pattern; 041 042 protected Converter<E> head; 043 044 protected String title = "Logback Log Messages"; 045 046 // It is the responsibility of derived classes to set 047 // this variable in their constructor to a default value. 048 protected CssBuilder cssBuilder; 049 050 // counter keeping track of the rows output 051 protected long counter = 0; 052 053 /** 054 * Set the <b>ConversionPattern </b> option. This is the string which controls 055 * formatting and consists of a mix of literal content and conversion 056 * specifiers. 057 */ 058 public void setPattern(String conversionPattern) { 059 pattern = conversionPattern; 060 } 061 062 /** 063 * Returns the value of the <b>ConversionPattern </b> option. 064 */ 065 public String getPattern() { 066 return pattern; 067 } 068 069 public CssBuilder getCssBuilder() { 070 return cssBuilder; 071 } 072 073 public void setCssBuilder(CssBuilder cssBuilder) { 074 this.cssBuilder = cssBuilder; 075 } 076 077 /** 078 * Parses the pattern and creates the Converter linked list. 079 */ 080 @Override 081 public void start() { 082 int errorCount = 0; 083 084 try { 085 Parser<E> p = new Parser<E>(pattern); 086 p.setContext(getContext()); 087 Node t = p.parse(); 088 this.head = p.compile(t, getEffectiveConverterMap()); 089 ConverterUtil.startConverters(this.head); 090 } catch (ScanException ex) { 091 addError("Incorrect pattern found", ex); 092 errorCount++; 093 } 094 095 if (errorCount == 0) { 096 super.started = true; 097 } 098 } 099 100 protected abstract Map<String, Supplier<DynamicConverter>> getDefaultConverterSupplierMap(); 101 102 /** 103 * Returns a map where the default converter map is merged with the map 104 * contained in the context. 105 */ 106 public Map<String, Supplier<DynamicConverter>> getEffectiveConverterMap() { 107 Map<String, Supplier<DynamicConverter>> effectiveMap = new HashMap<>(); 108 109 // add the least specific map fist 110 Map<String, Supplier<DynamicConverter>> defaultSupplierMap = getDefaultConverterSupplierMap(); 111 if (defaultSupplierMap != null) { 112 effectiveMap.putAll(defaultSupplierMap); 113 } 114 115 // contextMap is more specific than the default map 116 Context context = getContext(); 117 if (context != null) { 118 @SuppressWarnings("unchecked") 119 Map<String, Supplier<DynamicConverter>> contextMap = (Map<String, Supplier<DynamicConverter>>) context 120 .getObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS); 121 if (contextMap != null) { 122 effectiveMap.putAll(contextMap); 123 } 124 } 125 return effectiveMap; 126 } 127 128 /** 129 * The <b>Title </b> option takes a String value. This option sets the document 130 * title of the generated HTML document. 131 * 132 * <p> 133 * Defaults to 'Logback Log Messages'. 134 */ 135 public void setTitle(String title) { 136 this.title = title; 137 } 138 139 /** 140 * Returns the current value of the <b>Title </b> option. 141 */ 142 public String getTitle() { 143 return title; 144 } 145 146 /** 147 * Returns the content type output by this layout, i.e "text/html". 148 */ 149 @Override 150 public String getContentType() { 151 return "text/html"; 152 } 153 154 /** 155 * Returns appropriate HTML headers. 156 */ 157 @Override 158 public String getFileHeader() { 159 StringBuilder sbuf = new StringBuilder(); 160 sbuf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""); 161 sbuf.append(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); 162 sbuf.append(LINE_SEPARATOR); 163 sbuf.append("<html>"); 164 sbuf.append(LINE_SEPARATOR); 165 sbuf.append(" <head>"); 166 sbuf.append(LINE_SEPARATOR); 167 sbuf.append(" <title>"); 168 sbuf.append(title); 169 sbuf.append("</title>"); 170 sbuf.append(LINE_SEPARATOR); 171 172 cssBuilder.addCss(sbuf); 173 174 sbuf.append(LINE_SEPARATOR); 175 sbuf.append(" </head>"); 176 sbuf.append(LINE_SEPARATOR); 177 sbuf.append("<body>"); 178 sbuf.append(LINE_SEPARATOR); 179 180 return sbuf.toString(); 181 } 182 183 public String getPresentationHeader() { 184 StringBuilder sbuf = new StringBuilder(); 185 sbuf.append("<hr/>"); 186 sbuf.append(LINE_SEPARATOR); 187 sbuf.append("<p>Log session start time "); 188 sbuf.append(new java.util.Date()); 189 sbuf.append("</p><p></p>"); 190 sbuf.append(LINE_SEPARATOR); 191 sbuf.append(LINE_SEPARATOR); 192 sbuf.append("<table cellspacing=\"0\">"); 193 sbuf.append(LINE_SEPARATOR); 194 195 buildHeaderRowForTable(sbuf); 196 197 return sbuf.toString(); 198 } 199 200 private void buildHeaderRowForTable(StringBuilder sbuf) { 201 Converter<E> c = head; 202 String name; 203 sbuf.append("<tr class=\"header\">"); 204 sbuf.append(LINE_SEPARATOR); 205 while (c != null) { 206 name = computeConverterName(c); 207 if (name == null) { 208 c = c.getNext(); 209 continue; 210 } 211 sbuf.append("<td class=\""); 212 sbuf.append(computeConverterName(c)); 213 sbuf.append("\">"); 214 sbuf.append(computeConverterName(c)); 215 sbuf.append("</td>"); 216 sbuf.append(LINE_SEPARATOR); 217 c = c.getNext(); 218 } 219 sbuf.append("</tr>"); 220 sbuf.append(LINE_SEPARATOR); 221 } 222 223 public String getPresentationFooter() { 224 StringBuilder sbuf = new StringBuilder(); 225 sbuf.append("</table>"); 226 return sbuf.toString(); 227 } 228 229 /** 230 * Returns the appropriate HTML footers. 231 */ 232 @Override 233 public String getFileFooter() { 234 StringBuilder sbuf = new StringBuilder(); 235 sbuf.append(LINE_SEPARATOR); 236 sbuf.append("</body></html>"); 237 return sbuf.toString(); 238 } 239 240 protected void startNewTableIfLimitReached(StringBuilder sbuf) { 241 if (this.counter >= CoreConstants.TABLE_ROW_LIMIT) { 242 counter = 0; 243 sbuf.append("</table>"); 244 sbuf.append(LINE_SEPARATOR); 245 sbuf.append("<p></p>"); 246 sbuf.append("<table cellspacing=\"0\">"); 247 sbuf.append(LINE_SEPARATOR); 248 buildHeaderRowForTable(sbuf); 249 } 250 } 251 252 protected String computeConverterName(Converter<E> c) { 253 String className = c.getClass().getSimpleName(); 254 int index = className.indexOf("Converter"); 255 if (index == -1) { 256 return className; 257 } else { 258 return className.substring(0, index); 259 } 260 } 261 262}