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.joran;
015
016import static org.junit.Assert.assertEquals;
017import static org.junit.Assert.assertFalse;
018import static org.junit.Assert.assertNotNull;
019import static org.junit.Assert.assertNull;
020import static org.junit.Assert.assertTrue;
021
022import java.io.IOException;
023import java.text.SimpleDateFormat;
024import java.util.Date;
025
026import org.junit.Ignore;
027import org.junit.Test;
028import org.slf4j.MDC;
029
030import ch.qos.logback.classic.ClassicTestConstants;
031import ch.qos.logback.classic.Level;
032import ch.qos.logback.classic.Logger;
033import ch.qos.logback.classic.LoggerContext;
034import ch.qos.logback.classic.jul.JULHelper;
035import ch.qos.logback.classic.spi.ILoggingEvent;
036import ch.qos.logback.classic.turbo.DebugUsersTurboFilter;
037import ch.qos.logback.classic.turbo.NOPTurboFilter;
038import ch.qos.logback.classic.turbo.TurboFilter;
039import ch.qos.logback.core.ConsoleAppender;
040import ch.qos.logback.core.CoreConstants;
041import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
042import ch.qos.logback.core.joran.spi.JoranException;
043import ch.qos.logback.core.pattern.parser.Parser;
044import ch.qos.logback.core.read.ListAppender;
045import ch.qos.logback.core.spi.ScanException;
046import ch.qos.logback.core.status.Status;
047import ch.qos.logback.core.testUtil.RandomUtil;
048import ch.qos.logback.core.testUtil.StatusChecker;
049import ch.qos.logback.core.testUtil.StringListAppender;
050import ch.qos.logback.core.util.CachingDateFormatter;
051
052public class JoranConfiguratorTest {
053
054    LoggerContext loggerContext = new LoggerContext();
055    Logger logger = loggerContext.getLogger(this.getClass().getName());
056    Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
057    StatusChecker checker = new StatusChecker(loggerContext);
058    int diff = RandomUtil.getPositiveInt();
059
060    void configure(String file) throws JoranException {
061        JoranConfigurator jc = new JoranConfigurator();
062        jc.setContext(loggerContext);
063        loggerContext.putProperty("diff", "" + diff);
064        jc.doConfigure(file);
065    }
066
067    @Test
068    public void simpleList() throws JoranException {
069        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleList.xml");
070
071        Logger logger = loggerContext.getLogger(this.getClass().getName());
072        Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
073        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
074        assertEquals(0, listAppender.list.size());
075        String msg = "hello world";
076        logger.debug(msg);
077        assertEquals(1, listAppender.list.size());
078        ILoggingEvent le = (ILoggingEvent) listAppender.list.get(0);
079        assertEquals(msg, le.getMessage());
080    }
081
082    @Test
083    public void level() throws JoranException {
084        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleLevel.xml");
085        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
086        assertEquals(0, listAppender.list.size());
087        String msg = "hello world";
088        logger.debug(msg);
089        assertEquals(0, listAppender.list.size());
090    }
091
092    @Test
093    public void additivity() throws JoranException {
094        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "additivity.xml");
095        Logger logger = loggerContext.getLogger("additivityTest");
096        assertFalse(logger.isAdditive());
097    }
098
099    @Test
100    public void rootLoggerLevelSettingBySystemProperty() throws JoranException {
101        String propertyName = "logback.level";
102
103        System.setProperty(propertyName, "INFO");
104        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "rootLevelByProperty.xml");
105        // StatusPrinter.print(loggerContext);
106        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
107        assertEquals(0, listAppender.list.size());
108        String msg = "hello world";
109        logger.debug(msg);
110        assertEquals(0, listAppender.list.size());
111        System.clearProperty(propertyName);
112    }
113
114    @Test
115    public void loggerLevelSettingBySystemProperty() throws JoranException {
116        String propertyName = "logback.level";
117        System.setProperty(propertyName, "DEBUG");
118        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "loggerLevelByProperty.xml");
119        // StatusPrinter.print(loggerContext);
120        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
121        assertEquals(0, listAppender.list.size());
122        String msg = "hello world";
123        logger.debug(msg);
124        assertEquals(1, listAppender.list.size());
125        System.clearProperty(propertyName);
126    }
127
128    @Test
129    public void appenderRefSettingBySystemProperty() throws JoranException {
130        final String propertyName = "logback.appenderRef";
131        System.setProperty(propertyName, "A");
132        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "appenderRefByProperty.xml");
133        final Logger logger = loggerContext.getLogger("ch.qos.logback.classic.joran");
134        final ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) logger.getAppender("A");
135        assertEquals(0, listAppender.list.size());
136        final String msg = "hello world";
137        logger.info(msg);
138        assertEquals(1, listAppender.list.size());
139        System.clearProperty(propertyName);
140    }
141
142    @Test
143    public void statusListener() throws JoranException {
144        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "statusListener.xml");
145    }
146
147    @Test
148    public void contextRename() throws JoranException {
149        loggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
150        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "contextRename.xml");
151        assertEquals("wombat", loggerContext.getName());
152    }
153
154    @Test
155    public void eval() throws JoranException {
156        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "callerData.xml");
157
158        String msg = "hello world";
159        logger.debug("toto");
160        logger.debug(msg);
161
162        StringListAppender<ILoggingEvent> slAppender = (StringListAppender<ILoggingEvent>) loggerContext.getLogger("root").getAppender("STR_LIST");
163        assertNotNull(slAppender);
164        assertEquals(2, slAppender.strList.size());
165        assertTrue(slAppender.strList.get(0).contains(" DEBUG - toto"));
166
167        String str1 = slAppender.strList.get(1);
168        assertTrue(str1.contains("Caller+0"));
169        assertTrue(str1.contains(" DEBUG - hello world"));
170    }
171
172    @Test
173    public void turboFilter() throws JoranException {
174        // Although this test uses turbo filters, it only checks
175        // that Joran can see the xml element and create
176        // and place the relevant object correctly.
177        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turbo.xml");
178
179        TurboFilter filter = loggerContext.getTurboFilterList().get(0);
180        assertTrue(filter instanceof NOPTurboFilter);
181    }
182
183    @Test
184    public void testTurboFilterWithStringList() throws JoranException {
185        // Although this test uses turbo filters, it only checks
186        // that Joran can see <user> elements, and behave correctly
187        // that is call the addUser method and pass the correct values
188        // to that method.
189        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turbo2.xml");
190
191        // StatusPrinter.print(loggerContext.getStatusManager());
192
193        TurboFilter filter = loggerContext.getTurboFilterList().get(0);
194        assertTrue(filter instanceof DebugUsersTurboFilter);
195        DebugUsersTurboFilter dutf = (DebugUsersTurboFilter) filter;
196        assertEquals(2, dutf.getUsers().size());
197    }
198
199    @Test
200    public void testLevelFilter() throws JoranException {
201        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "levelFilter.xml");
202
203        // StatusPrinter.print(loggerContext);
204
205        logger.warn("hello");
206        logger.error("to be ignored");
207
208        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
209
210        assertNotNull(listAppender);
211        assertEquals(1, listAppender.list.size());
212        ILoggingEvent back = listAppender.list.get(0);
213        assertEquals(Level.WARN, back.getLevel());
214        assertEquals("hello", back.getMessage());
215    }
216
217    @Test
218    public void testEvaluatorFilter() throws JoranException {
219        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilter.xml");
220
221        // StatusPrinter.print(loggerContext);
222
223        logger.warn("hello");
224        logger.error("to be ignored");
225
226        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
227
228        assertNotNull(listAppender);
229        assertEquals(1, listAppender.list.size());
230        ILoggingEvent back = listAppender.list.get(0);
231        assertEquals(Level.WARN, back.getLevel());
232        assertEquals("hello", back.getMessage());
233    }
234
235    @Test
236    public void testTurboDynamicThreshold() throws JoranException {
237        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turboDynamicThreshold.xml");
238
239        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
240        assertEquals(0, listAppender.list.size());
241
242        // this one should be denied
243        MDC.put("userId", "user1");
244        logger.debug("hello user1");
245        // this one should log
246        MDC.put("userId", "user2");
247        logger.debug("hello user2");
248
249        assertEquals(1, listAppender.list.size());
250        ILoggingEvent le = (ILoggingEvent) listAppender.list.get(0);
251        assertEquals("hello user2", le.getMessage());
252    }
253
254    @Test
255    public void testTurboDynamicThreshold2() throws JoranException {
256        configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turboDynamicThreshold2.xml");
257
258        ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
259        assertEquals(0, listAppender.list.size());
260
261        // this one should log
262        MDC.put("userId", "user1");
263        logger.debug("hello user1");
264        // this one should log
265        MDC.put("userId", "user2");
266        logger.debug("hello user2");
267        // this one should fail
268        MDC.put("userId", "user3");
269        logger.debug("hello user3");
270
271        assertEquals(2, listAppender.list.size());
272        ILoggingEvent le = (ILoggingEvent) listAppender.list.get(0);
273        assertEquals("hello user1", le.getMessage());
274        le = (ILoggingEvent) listAppender.list.get(1);
275        assertEquals("hello user2", le.getMessage());
276    }
277
278    @Test
279    public void timestamp() throws JoranException, IOException, InterruptedException {
280
281        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "timestamp-context.xml";
282        configure(configFileAsStr);
283
284        String r = loggerContext.getProperty("testTimestamp");
285        assertNotNull(r);
286        CachingDateFormatter sdf = new CachingDateFormatter("yyyy-MM");
287        String expected = sdf.format(System.currentTimeMillis());
288        assertEquals("expected \"" + expected + "\" but got " + r, expected, r);
289    }
290
291    @Test
292    public void timestampLocal() throws JoranException, IOException, InterruptedException {
293
294        String sysProp = "ch.qos.logback.classic.joran.JoranConfiguratorTest.timestampLocal";
295        System.setProperty(sysProp, "");
296
297        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "timestamp-local.xml";
298        configure(configFileAsStr);
299
300        // It's hard to test the local variable has been set, as it's not
301        // visible from here. But instead we test that it's not set in the
302        // context. And check that a system property has been replaced with the
303        // contents of the local variable
304
305        String r = loggerContext.getProperty("testTimestamp");
306        assertNull(r);
307
308        String expected = "today is " + new SimpleDateFormat("yyyy-MM").format(new Date());
309        String sysPropValue = System.getProperty(sysProp);
310        assertEquals(expected, sysPropValue);
311    }
312
313    @Test
314    public void encoderCharset() throws JoranException, IOException, InterruptedException {
315
316        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "encoderCharset.xml";
317        configure(configFileAsStr);
318
319        ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) root.getAppender("CONSOLE");
320        assertNotNull(consoleAppender);
321        LayoutWrappingEncoder<ILoggingEvent> encoder = (LayoutWrappingEncoder<ILoggingEvent>) consoleAppender.getEncoder();
322
323        assertEquals("UTF-8", encoder.getCharset().displayName());
324
325        StatusChecker checker = new StatusChecker(loggerContext);
326        checker.assertIsErrorFree();
327    }
328
329    void verifyJULLevel(String loggerName, Level expectedLevel) {
330        java.util.logging.Logger julLogger = JULHelper.asJULLogger(loggerName);
331        java.util.logging.Level julLevel = julLogger.getLevel();
332
333        if (expectedLevel == null) {
334            assertNull(julLevel);
335        } else {
336            assertEquals(JULHelper.asJULLevel(expectedLevel), julLevel);
337        }
338
339    }
340
341    @Test
342    public void levelChangePropagator0() throws JoranException, IOException, InterruptedException {
343        String loggerName = "changePropagator0" + diff;
344        java.util.logging.Logger.getLogger(loggerName).setLevel(java.util.logging.Level.INFO);
345        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator0.xml";
346        configure(configFileAsStr);
347        StatusChecker checker = new StatusChecker(loggerContext);
348        checker.assertIsErrorFree();
349        verifyJULLevel(loggerName, null);
350        verifyJULLevel("a.b.c." + diff, Level.WARN);
351        verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE);
352    }
353
354    @Test
355    public void levelChangePropagator1() throws JoranException, IOException, InterruptedException {
356        String loggerName = "changePropagator1" + diff;
357        java.util.logging.Logger logger1 = java.util.logging.Logger.getLogger(loggerName);
358        logger1.setLevel(java.util.logging.Level.INFO);
359        verifyJULLevel(loggerName, Level.INFO);
360        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator1.xml";
361        configure(configFileAsStr);
362        StatusChecker checker = new StatusChecker(loggerContext);
363        checker.assertIsErrorFree();
364        verifyJULLevel(loggerName, Level.INFO); //
365        verifyJULLevel("a.b.c." + diff, Level.WARN);
366        verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE);
367    }
368
369    @Test
370    @Ignore
371    public void onConsoleRetro() throws JoranException, IOException, InterruptedException {
372        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/onConsoleRetro.xml";
373        configure(configFileAsStr);
374        System.out.println("xxxxxxxxxxxxx");
375        Thread.sleep(400);
376
377        loggerContext.reset();
378        configure(configFileAsStr);
379    }
380
381    @Test
382    public void lbcore193() throws JoranException {
383        String configFileAsStr = ClassicTestConstants.ISSUES_PREFIX + "lbcore193.xml";
384        configure(configFileAsStr);
385        checker.asssertContainsException(ScanException.class);
386        checker.assertContainsMatch(Status.ERROR, "Expecting RIGHT_PARENTHESIS token but got null");
387        checker.assertContainsMatch(Status.ERROR, "See also " + Parser.MISSING_RIGHT_PARENTHESIS);
388    }
389
390    @Test
391    public void properties() throws JoranException {
392        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "properties.xml";
393        assertNull(System.getProperty("sys"));
394
395        configure(configFileAsStr);
396        assertNotNull(loggerContext.getProperty(CoreConstants.HOSTNAME_KEY));
397        assertNull(loggerContext.getProperty("transientKey1"));
398        assertNull(loggerContext.getProperty("transientKey2"));
399        assertEquals("node0", loggerContext.getProperty("nodeId"));
400        assertEquals("tem", System.getProperty("sys"));
401        assertNotNull(loggerContext.getProperty("path"));
402        checker.assertIsErrorFree();
403    }
404
405    @Test
406    public void hostnameProperty() throws JoranException {
407        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "hostnameProperty.xml";
408        configure(configFileAsStr);
409        assertEquals("A", loggerContext.getProperty(CoreConstants.HOSTNAME_KEY));
410    }
411
412    // see also http://jira.qos.ch/browse/LBCORE-254
413    @Test
414    public void sysProps() throws JoranException {
415        System.setProperty("k.lbcore254", ClassicTestConstants.ISSUES_PREFIX + "lbcore254");
416        JoranConfigurator configurator = new JoranConfigurator();
417        configurator.setContext(loggerContext);
418        configurator.doConfigure(ClassicTestConstants.ISSUES_PREFIX + "lbcore254.xml");
419
420        checker.assertIsErrorFree();
421    }
422
423    @Test
424    public void packageDataDisabledByConfigAttribute() throws JoranException {
425        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "packagingDataDisabled.xml";
426        configure(configFileAsStr);
427        assertFalse(loggerContext.isPackagingDataEnabled());
428    }
429
430    @Test
431    public void packageDataEnabledByConfigAttribute() throws JoranException {
432        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "packagingDataEnabled.xml";
433        configure(configFileAsStr);
434        assertTrue(loggerContext.isPackagingDataEnabled());
435    }
436
437    @Test
438    public void valueOfConvention() throws JoranException {
439        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "valueOfConvention.xml";
440        configure(configFileAsStr);
441        checker.assertIsWarningOrErrorFree();
442    }
443    
444    @Test
445    public void shutdownHookTest() throws JoranException {
446        String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1162.xml";
447        loggerContext.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX+"logback_issue_1162/");
448        configure(configFileAsStr);
449        assertNotNull(loggerContext.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD));
450    }
451
452}