View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.html;
15  
16  import java.io.ByteArrayInputStream;
17  import java.util.List;
18  
19  import org.dom4j.Document;
20  import org.dom4j.Element;
21  import org.dom4j.io.SAXReader;
22  import org.junit.jupiter.api.AfterEach;
23  import org.junit.jupiter.api.BeforeEach;
24  import org.junit.jupiter.api.Disabled;
25  import org.junit.jupiter.api.Test;
26  import org.xml.sax.EntityResolver;
27  
28  import ch.qos.logback.classic.ClassicTestConstants;
29  import ch.qos.logback.classic.Level;
30  import ch.qos.logback.classic.Logger;
31  import ch.qos.logback.classic.LoggerContext;
32  import ch.qos.logback.classic.joran.JoranConfigurator;
33  import ch.qos.logback.classic.spi.PubThrowableProxy;
34  import ch.qos.logback.classic.spi.ILoggingEvent;
35  import ch.qos.logback.classic.spi.LoggingEvent;
36  import ch.qos.logback.classic.spi.StackTraceElementProxy;
37  import ch.qos.logback.classic.spi.ThrowableProxy;
38  import ch.qos.logback.core.CoreConstants;
39  import ch.qos.logback.core.joran.spi.JoranException;
40  import ch.qos.logback.core.testUtil.StringListAppender;
41  import ch.qos.logback.core.util.StatusPrinter;
42  
43  import static org.junit.jupiter.api.Assertions.assertEquals;
44  import static org.junit.jupiter.api.Assertions.assertFalse;
45  import static org.junit.jupiter.api.Assertions.assertNotNull;
46  import static org.junit.jupiter.api.Assertions.assertTrue;
47  
48  public class HTMLLayoutTest {
49  
50      LoggerContext lc;
51      Logger root;
52      HTMLLayout layout;
53  
54      @BeforeEach
55      public void setUp() throws Exception {
56          lc = new LoggerContext();
57          lc.setName("default");
58  
59          layout = new HTMLLayout();
60          layout.setThrowableRenderer(new DefaultThrowableRenderer());
61          layout.setContext(lc);
62          layout.setPattern("%level%thread%msg");
63          layout.start();
64  
65          root = lc.getLogger(Logger.ROOT_LOGGER_NAME);
66  
67      }
68  
69      @AfterEach
70      public void tearDown() throws Exception {
71          lc = null;
72          layout = null;
73      }
74  
75      @Test
76      public void testHeader() throws Exception {
77          String header = layout.getFileHeader();
78          // System.out.println(header);
79  
80          Document doc = parseOutput(header + "</body></html>");
81          Element rootElement = doc.getRootElement();
82          assertNotNull(rootElement.element("body"));
83  
84      }
85  
86      @Test
87      public void testPresentationHeader() throws Exception {
88          String header = layout.getFileHeader();
89          String presentationHeader = layout.getPresentationHeader();
90          header = header + presentationHeader;
91          // System.out.println(header);
92  
93          Document doc = parseOutput(header + "</table></body></html>");
94          Element rootElement = doc.getRootElement();
95          Element bodyElement = rootElement.element("body");
96          Element tableElement = bodyElement.element("table");
97          Element trElement = tableElement.element("tr");
98          List<Element> elementList = trElement.elements();
99          assertEquals("Level", elementList.get(0).getText());
100         assertEquals("Thread", elementList.get(1).getText());
101         assertEquals("Message", elementList.get(2).getText());
102     }
103 
104     @Test
105     public void testAppendThrowable() throws Exception {
106         StringBuilder buf = new StringBuilder();
107         PubThrowableProxy tp = new PubThrowableProxy();
108         tp.setClassName("test1");
109         tp.setMessage("msg1");
110 
111         StackTraceElement ste1 = new StackTraceElement("c1", "m1", "f1", 1);
112         StackTraceElement ste2 = new StackTraceElement("c2", "m2", "f2", 2);
113 
114         StackTraceElementProxy[] stepArray = { new StackTraceElementProxy(ste1), new StackTraceElementProxy(ste2) };
115         tp.setStackTraceElementProxyArray(stepArray);
116         DefaultThrowableRenderer renderer = (DefaultThrowableRenderer) layout.getThrowableRenderer();
117 
118         renderer.render(buf, tp);
119         System.out.println(buf.toString());
120         String[] result = buf.toString().split(CoreConstants.LINE_SEPARATOR);
121         System.out.println(result[0]);
122         assertEquals("test1: msg1", result[0]);
123         assertEquals(DefaultThrowableRenderer.TRACE_PREFIX + "at c1.m1(f1:1)", result[1]);
124     }
125 
126     @Test
127     public void testDoLayout() throws Exception {
128         ILoggingEvent le = createLoggingEvent();
129 
130         String result = layout.getFileHeader();
131         result += layout.getPresentationHeader();
132         result += layout.doLayout(le);
133         result += layout.getPresentationFooter();
134         result += layout.getFileFooter();
135 
136         Document doc = parseOutput(result);
137         Element rootElement = doc.getRootElement();
138         rootElement.toString();
139 
140         // the rest of this test is very dependent of the output generated
141         // by HTMLLayout. Given that the XML parser already verifies
142         // that the result conforms to xhtml-strict, we may want to
143         // skip the assertions below. However, the assertions below are another
144         // *independent* way to check the output format.
145 
146         // head, body
147         assertEquals(2, rootElement.elements().size());
148         Element bodyElement = (Element) rootElement.elements().get(1);
149         Element tableElement = (Element) bodyElement.elements().get(3);
150         assertEquals("table", tableElement.getName());
151         Element trElement = (Element) tableElement.elements().get(1);
152         {
153             Element tdElement = (Element) trElement.elements().get(0);
154             assertEquals("DEBUG", tdElement.getText());
155         }
156         {
157             Element tdElement = (Element) trElement.elements().get(1);
158             String regex = ClassicTestConstants.NAKED_MAIN_REGEX;
159             System.out.println(tdElement.getText());
160             assertTrue(tdElement.getText().matches(regex));
161         }
162         {
163             Element tdElement = (Element) trElement.elements().get(2);
164             assertEquals("test message", tdElement.getText());
165         }
166     }
167 
168     @Test
169     public void layoutWithException() throws Exception {
170         layout.setPattern("%level %thread %msg %ex");
171         LoggingEvent le = createLoggingEvent();
172         le.setThrowableProxy(new ThrowableProxy(new Exception("test Exception")));
173         String result = layout.doLayout(le);
174 
175         String stringToParse = layout.getFileHeader();
176         stringToParse = stringToParse + layout.getPresentationHeader();
177         stringToParse += result;
178         stringToParse += "</table></body></html>";
179 
180         // System.out.println(stringToParse);
181 
182         Document doc = parseOutput(stringToParse);
183         Element rootElement = doc.getRootElement();
184         Element bodyElement = rootElement.element("body");
185         Element tableElement = bodyElement.element("table");
186         List<Element> trElementList = tableElement.elements();
187         Element exceptionRowElement = trElementList.get(2);
188         Element exceptionElement = exceptionRowElement.element("td");
189 
190         assertEquals(3, tableElement.elements().size());
191         assertTrue(exceptionElement.getText().contains("java.lang.Exception: test Exception"));
192     }
193 
194     @Test
195     @Disabled
196     public void rawLimit() throws Exception {
197         StringBuilder sb = new StringBuilder();
198         String header = layout.getFileHeader();
199         assertTrue(header.startsWith(
200                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"));
201         sb.append(header);
202         sb.append(layout.getPresentationHeader());
203         for (int i = 0; i < CoreConstants.TABLE_ROW_LIMIT * 3; i++) {
204             sb.append(layout.doLayout(
205                     new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message" + i, null, null)));
206         }
207         sb.append(layout.getPresentationFooter());
208         sb.append(layout.getFileFooter());
209         // check that the output adheres to xhtml-strict.dtd
210         parseOutput(sb.toString());
211     }
212 
213     private LoggingEvent createLoggingEvent() {
214         return new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message", null, null);
215     }
216 
217     Document parseOutput(String output) throws Exception {
218         EntityResolver resolver = new XHTMLEntityResolver();
219         SAXReader reader = new SAXReader();
220         reader.setValidation(true);
221         reader.setEntityResolver(resolver);
222         return reader.read(new ByteArrayInputStream(output.getBytes()));
223     }
224 
225     void configure(String file) throws JoranException {
226         JoranConfigurator jc = new JoranConfigurator();
227         jc.setContext(lc);
228         jc.doConfigure(file);
229     }
230 
231     @Test
232     public void testConversionRuleSupportInHtmlLayout() throws JoranException {
233         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/htmlLayout0.xml");
234 
235         root.getAppender("LIST");
236         String msg = "Simon says";
237         root.debug(msg);
238         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
239         assertNotNull(sla);
240         StatusPrinter.print(lc);
241         assertEquals(1, sla.strList.size());
242         assertFalse(sla.strList.get(0).contains("PARSER_ERROR"));
243     }
244 }