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 static org.junit.Assert.assertEquals; 017import static org.junit.Assert.assertFalse; 018import static org.junit.Assert.assertNotNull; 019import static org.junit.Assert.assertTrue; 020 021import java.io.ByteArrayInputStream; 022import java.util.List; 023 024import org.dom4j.Document; 025import org.dom4j.Element; 026import org.dom4j.io.SAXReader; 027import org.junit.After; 028import org.junit.Before; 029import org.junit.Ignore; 030import org.junit.Test; 031import org.xml.sax.EntityResolver; 032 033import ch.qos.logback.classic.ClassicTestConstants; 034import ch.qos.logback.classic.Level; 035import ch.qos.logback.classic.Logger; 036import ch.qos.logback.classic.LoggerContext; 037import ch.qos.logback.classic.joran.JoranConfigurator; 038import ch.qos.logback.classic.spi.DummyThrowableProxy; 039import ch.qos.logback.classic.spi.ILoggingEvent; 040import ch.qos.logback.classic.spi.LoggingEvent; 041import ch.qos.logback.classic.spi.StackTraceElementProxy; 042import ch.qos.logback.classic.spi.ThrowableProxy; 043import ch.qos.logback.core.CoreConstants; 044import ch.qos.logback.core.joran.spi.JoranException; 045import ch.qos.logback.core.testUtil.StringListAppender; 046import ch.qos.logback.core.util.StatusPrinter; 047 048public class HTMLLayoutTest { 049 050 LoggerContext lc; 051 Logger root; 052 HTMLLayout layout; 053 054 @Before 055 public void setUp() throws Exception { 056 lc = new LoggerContext(); 057 lc.setName("default"); 058 059 layout = new HTMLLayout(); 060 layout.setThrowableRenderer(new DefaultThrowableRenderer()); 061 layout.setContext(lc); 062 layout.setPattern("%level%thread%msg"); 063 layout.start(); 064 065 root = lc.getLogger(Logger.ROOT_LOGGER_NAME); 066 067 } 068 069 @After 070 public void tearDown() throws Exception { 071 lc = null; 072 layout = null; 073 } 074 075 @Test 076 public void testHeader() throws Exception { 077 String header = layout.getFileHeader(); 078 // System.out.println(header); 079 080 Document doc = parseOutput(header + "</body></html>"); 081 Element rootElement = doc.getRootElement(); 082 assertNotNull(rootElement.element("body")); 083 } 084 085 @Test 086 public void testPresentationHeader() throws Exception { 087 String header = layout.getFileHeader(); 088 String presentationHeader = layout.getPresentationHeader(); 089 header = header + presentationHeader; 090 // System.out.println(header); 091 092 Document doc = parseOutput(header + "</table></body></html>"); 093 Element rootElement = doc.getRootElement(); 094 Element bodyElement = rootElement.element("body"); 095 Element tableElement = bodyElement.element("table"); 096 Element trElement = tableElement.element("tr"); 097 List<Element> elementList = trElement.elements(); 098 assertEquals("Level", elementList.get(0).getText()); 099 assertEquals("Thread", elementList.get(1).getText()); 100 assertEquals("Message", elementList.get(2).getText()); 101 } 102 103 @Test 104 public void testAppendThrowable() throws Exception { 105 StringBuilder buf = new StringBuilder(); 106 DummyThrowableProxy tp = new DummyThrowableProxy(); 107 tp.setClassName("test1"); 108 tp.setMessage("msg1"); 109 110 StackTraceElement ste1 = new StackTraceElement("c1", "m1", "f1", 1); 111 StackTraceElement ste2 = new StackTraceElement("c2", "m2", "f2", 2); 112 113 StackTraceElementProxy[] stepArray = { new StackTraceElementProxy(ste1), new StackTraceElementProxy(ste2) }; 114 tp.setStackTraceElementProxyArray(stepArray); 115 DefaultThrowableRenderer renderer = (DefaultThrowableRenderer) layout.getThrowableRenderer(); 116 117 renderer.render(buf, tp); 118 System.out.println(buf.toString()); 119 String[] result = buf.toString().split(CoreConstants.LINE_SEPARATOR); 120 System.out.println(result[0]); 121 assertEquals("test1: msg1", result[0]); 122 assertEquals(DefaultThrowableRenderer.TRACE_PREFIX + "at c1.m1(f1:1)", result[1]); 123 } 124 125 @Test 126 public void testDoLayout() throws Exception { 127 ILoggingEvent le = createLoggingEvent(); 128 129 String result = layout.getFileHeader(); 130 result += layout.getPresentationHeader(); 131 result += layout.doLayout(le); 132 result += layout.getPresentationFooter(); 133 result += layout.getFileFooter(); 134 135 Document doc = parseOutput(result); 136 Element rootElement = doc.getRootElement(); 137 rootElement.toString(); 138 139 // the rest of this test is very dependent of the output generated 140 // by HTMLLayout. Given that the XML parser already verifies 141 // that the result conforms to xhtml-strict, we may want to 142 // skip the assertions below. However, the assertions below are another 143 // *independent* way to check the output format. 144 145 // head, body 146 assertEquals(2, rootElement.elements().size()); 147 Element bodyElement = (Element) rootElement.elements().get(1); 148 Element tableElement = (Element) bodyElement.elements().get(3); 149 assertEquals("table", tableElement.getName()); 150 Element trElement = (Element) tableElement.elements().get(1); 151 { 152 Element tdElement = (Element) trElement.elements().get(0); 153 assertEquals("DEBUG", tdElement.getText()); 154 } 155 { 156 Element tdElement = (Element) trElement.elements().get(1); 157 String regex = ClassicTestConstants.NAKED_MAIN_REGEX; 158 System.out.println(tdElement.getText()); 159 assertTrue(tdElement.getText().matches(regex)); 160 } 161 { 162 Element tdElement = (Element) trElement.elements().get(2); 163 assertEquals("test message", tdElement.getText()); 164 } 165 } 166 167 @Test 168 public void layoutWithException() throws Exception { 169 layout.setPattern("%level %thread %msg %ex"); 170 LoggingEvent le = createLoggingEvent(); 171 le.setThrowableProxy(new ThrowableProxy(new Exception("test Exception"))); 172 String result = layout.doLayout(le); 173 174 String stringToParse = layout.getFileHeader(); 175 stringToParse = stringToParse + layout.getPresentationHeader(); 176 stringToParse += result; 177 stringToParse += "</table></body></html>"; 178 179 // System.out.println(stringToParse); 180 181 Document doc = parseOutput(stringToParse); 182 Element rootElement = doc.getRootElement(); 183 Element bodyElement = rootElement.element("body"); 184 Element tableElement = bodyElement.element("table"); 185 List<Element> trElementList = tableElement.elements(); 186 Element exceptionRowElement = trElementList.get(2); 187 Element exceptionElement = exceptionRowElement.element("td"); 188 189 assertEquals(3, tableElement.elements().size()); 190 assertTrue(exceptionElement.getText().contains("java.lang.Exception: test Exception")); 191 } 192 193 @Test 194 @Ignore 195 public void rawLimit() throws Exception { 196 StringBuilder sb = new StringBuilder(); 197 String header = layout.getFileHeader(); 198 assertTrue(header.startsWith("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">")); 199 sb.append(header); 200 sb.append(layout.getPresentationHeader()); 201 for (int i = 0; i < CoreConstants.TABLE_ROW_LIMIT * 3; i++) { 202 sb.append(layout.doLayout(new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message" + i, null, null))); 203 } 204 sb.append(layout.getPresentationFooter()); 205 sb.append(layout.getFileFooter()); 206 // check that the output adheres to xhtml-strict.dtd 207 parseOutput(sb.toString()); 208 } 209 210 private LoggingEvent createLoggingEvent() { 211 return new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message", null, null); 212 } 213 214 Document parseOutput(String output) throws Exception { 215 EntityResolver resolver = new XHTMLEntityResolver(); 216 SAXReader reader = new SAXReader(); 217 reader.setValidation(true); 218 reader.setEntityResolver(resolver); 219 return reader.read(new ByteArrayInputStream(output.getBytes())); 220 } 221 222 void configure(String file) throws JoranException { 223 JoranConfigurator jc = new JoranConfigurator(); 224 jc.setContext(lc); 225 jc.doConfigure(file); 226 } 227 228 @Test 229 public void testConversionRuleSupportInHtmlLayout() throws JoranException { 230 configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/htmlLayout0.xml"); 231 232 root.getAppender("LIST"); 233 String msg = "Simon says"; 234 root.debug(msg); 235 StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST"); 236 assertNotNull(sla); 237 StatusPrinter.print(lc); 238 assertEquals(1, sla.strList.size()); 239 assertFalse(sla.strList.get(0).contains("PARSER_ERROR")); 240 } 241}