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}