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;
15  
16  import ch.qos.logback.classic.joran.JoranConfigurator;
17  import ch.qos.logback.classic.pattern.ConverterTest;
18  import ch.qos.logback.classic.pattern.ExceptionalConverter2;
19  import ch.qos.logback.classic.spi.ILoggingEvent;
20  import ch.qos.logback.classic.spi.LoggingEvent;
21  import ch.qos.logback.classic.pattern.SampleConverter;
22  import ch.qos.logback.classic.util.LogbackMDCAdapter;
23  import ch.qos.logback.core.Context;
24  import ch.qos.logback.core.joran.spi.JoranException;
25  import ch.qos.logback.core.pattern.PatternLayoutBase;
26  import ch.qos.logback.core.pattern.parser.test.AbstractPatternLayoutBaseTest;
27  import ch.qos.logback.core.spi.ScanException;
28  import ch.qos.logback.core.testUtil.RandomUtil;
29  import ch.qos.logback.core.testUtil.StringListAppender;
30  import ch.qos.logback.core.util.OptionHelper;
31  import ch.qos.logback.core.util.StatusPrinter;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  import org.slf4j.MDC;
35  
36  import static ch.qos.logback.classic.ClassicTestConstants.ISO_REGEX;
37  import static ch.qos.logback.classic.ClassicTestConstants.MAIN_REGEX;
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  import static org.junit.jupiter.api.Assertions.assertNotNull;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  
42  import java.time.Instant;
43  
44  public class PatternLayoutTest extends AbstractPatternLayoutBaseTest<ILoggingEvent> {
45  
46      private PatternLayout pl = new PatternLayout();
47      private LoggerContext loggerContext = new LoggerContext();
48  
49      LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter();
50      Logger logger = loggerContext.getLogger(ConverterTest.class);
51      Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
52  
53      int diff = RandomUtil.getPositiveInt();
54  
55      String aMessage = "Some message";
56  
57      Exception ex = new Exception("Bogus exception");
58  
59  
60  
61      @BeforeEach
62      public void setUp() {
63          loggerContext.setMDCAdapter(logbackMDCAdapter);
64          pl.setContext(loggerContext);
65          //le = makeLoggingEvent(aMessage, ex);
66      }
67  
68      /**
69       * Circumvent JMPS issue: java.lang.NoClassDefFoundError: ch/qos/logback/core/pattern/ExceptionalConverter
70       * Is logback-clasic not open to logback-core?
71       * @return
72       */
73      protected String getExceptionalConverterClassName() {
74          return ExceptionalConverter2.class.getName();
75      }
76  
77      LoggingEvent makeLoggingEvent(String msg, Exception ex) {
78          return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO,
79                  msg, ex, null);
80      }
81  
82      public ILoggingEvent getEventObject() {
83          return makeLoggingEvent("Some message", null);
84      }
85  
86      public PatternLayoutBase<ILoggingEvent> getPatternLayoutBase() {
87          return new PatternLayout();
88      }
89  
90      @Test
91      public void testOK() {
92          pl.setPattern("%d %le [%t] %lo{30} - %m%n");
93          pl.start();
94          String val = pl.doLayout(getEventObject());
95          // 2006-02-01 22:38:06,212 INFO [main] c.q.l.pattern.ConverterTest - Some
96          // message
97          // 2010-12-29 19:04:26,137 INFO [pool-1-thread-47] c.q.l.c.pattern.ConverterTest
98          // - Some message
99          String regex = ISO_REGEX + " INFO " + MAIN_REGEX + " c.q.l.c.pattern.ConverterTest - Some message\\s*";
100 
101         assertTrue( val.matches(regex), "val=" + val);
102     }
103 
104     @Test
105     public void testNoExeptionHandler() {
106         pl.setPattern("%m%n");
107         pl.start();
108         String val = pl.doLayout(makeLoggingEvent(aMessage, ex));
109         assertTrue(val.contains("java.lang.Exception: Bogus exception"));
110     }
111 
112     @Test
113     public void testCompositePattern() {
114         pl.setPattern("%-56(%d %lo{20}) - %m%n");
115         pl.start();
116         String val = pl.doLayout(getEventObject());
117         // 2008-03-18 21:55:54,250 c.q.l.c.pattern.ConverterTest - Some message
118         String regex = ISO_REGEX + " c.q.l.c.p.ConverterTest          - Some message\\s*";
119         assertTrue(val.matches(regex));
120     }
121 
122     @Test
123     public void contextProperty() {
124         pl.setPattern("%property{a}");
125         pl.start();
126         loggerContext.putProperty("a", "b");
127 
128         String val = pl.doLayout(getEventObject());
129         assertEquals("b", val);
130     }
131 
132     @Test
133     public void testNopExeptionHandler() {
134         pl.setPattern("%nopex %m%n");
135         pl.start();
136         String val = pl.doLayout(makeLoggingEvent(aMessage, ex));
137         assertTrue(!val.contains("java.lang.Exception: Bogus exception"));
138     }
139 
140     @Test
141     public void testWithParenthesis() {
142         pl.setPattern("\\(%msg:%msg\\) %msg");
143         pl.start();
144         LoggingEvent le = makeLoggingEvent(aMessage, null);
145         String val = pl.doLayout(le);
146         assertEquals("(Some message:Some message) Some message", val);
147     }
148 
149     @Test
150     public void testWithLettersComingFromLog4j() {
151         // Letters: p = level and c = logger
152         pl.setPattern("%d %p [%t] %c{30} - %m%n");
153         pl.start();
154         String val = pl.doLayout(getEventObject());
155         // 2006-02-01 22:38:06,212 INFO [main] c.q.l.pattern.ConverterTest - Some
156         // message
157         String regex = ClassicTestConstants.ISO_REGEX + " INFO " + MAIN_REGEX
158                 + " c.q.l.c.pattern.ConverterTest - Some message\\s*";
159         assertTrue(val.matches(regex));
160     }
161 
162     @Test
163     public void mdcWithDefaultValue() throws ScanException {
164         String pattern = "%msg %mdc{foo1} %mdc{bar:-[null]}";
165         pl.setPattern(OptionHelper.substVars(pattern, loggerContext));
166         pl.start();
167 
168         String key = "foo1";
169 
170         logbackMDCAdapter.put(key, key);
171         try {
172             String val = pl.doLayout(getEventObject());
173             assertEquals("Some message foo1 [null]", val);
174         } finally {
175             logbackMDCAdapter.remove(key);
176         }
177     }
178 
179     @Test
180     public void contextNameTest() {
181         pl.setPattern("%contextName");
182         loggerContext.setName("aValue");
183         pl.start();
184         String val = pl.doLayout(getEventObject());
185         assertEquals("aValue", val);
186     }
187 
188     @Test
189     public void cnTest() {
190         pl.setPattern("%cn");
191         loggerContext.setName("aValue");
192         pl.start();
193         String val = pl.doLayout(getEventObject());
194         assertEquals("aValue", val);
195     }
196 
197     @Test
198     public void micros() {
199         verifyMicros(122_891_479, "2011-12-03 10:15:30.122 891 Some message");
200         verifyMicros(122_091_479, "2011-12-03 10:15:30.122 091 Some message");
201         verifyMicros(122_001_479, "2011-12-03 10:15:30.122 001 Some message");
202     }
203 
204     void verifyMicros(int nanos, String expected) {
205         Instant instant = Instant.parse("2011-12-03T10:15:30Z");
206         instant = instant.plusNanos(nanos);
207         LoggingEvent le = makeLoggingEvent(aMessage, null);
208         le.setInstant(instant);
209 
210         pl.setPattern("%date{yyyy-MM-dd HH:mm:ss.SSS, UTC} %micros %message%nopex");
211         pl.start();
212 
213         String val = pl.doLayout(le);
214         assertEquals(expected, val);
215     }
216 
217     @Override
218     public Context getContext() {
219         return loggerContext;
220     }
221 
222     void configure(String file) throws JoranException {
223         JoranConfigurator jc = new JoranConfigurator();
224         jc.setContext(loggerContext);
225         jc.doConfigure(file);
226     }
227 
228     @Test
229     public void testConversionRuleSupportInPatternLayout() throws JoranException {
230         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/patternLayout0.xml");
231         root.getAppender("LIST");
232         String msg = "Simon says";
233         logger.debug(msg);
234         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
235         assertNotNull(sla);
236         assertEquals(1, sla.strList.size());
237         assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0));
238     }
239 
240     @Test
241     public void testConversionRuleAtEnd() throws JoranException {
242         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/conversionRuleAtEnd.xml");
243         root.getAppender("LIST");
244         String msg = "testConversionRuleAtEnd";
245         logger.debug(msg);
246         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
247         assertNotNull(sla);
248         assertEquals(1, sla.strList.size());
249         assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0));
250     }
251 
252     @Test
253     public void testConversionRuleInIncluded() throws JoranException {
254         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/conversionRuleTop0.xml");
255         StatusPrinter.print(loggerContext);
256         root.getAppender("LIST");
257         String msg = "testConversionRuleInIncluded";
258         logger.debug(msg);
259         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
260         assertNotNull(sla);
261         assertEquals(1, sla.strList.size());
262         assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0));
263     }
264 
265     @Test
266     public void smokeReplace() {
267         pl.setPattern("%replace(a1234b){'\\d{4}', 'XXXX'}");
268         pl.start();
269         String val = pl.doLayout(getEventObject());
270         assertEquals("aXXXXb", val);
271     }
272 
273     @Test
274     public void replaceNewline() throws ScanException {
275         String pattern = "%replace(A\nB){'\n', '\n\t'}";
276         String substPattern = OptionHelper.substVars(pattern, null, loggerContext);
277         assertEquals(pattern, substPattern);
278         pl.setPattern(substPattern);
279         pl.start();
280         //StatusPrinter.print(lc);
281         String val = pl.doLayout(makeLoggingEvent("", null));
282         assertEquals("A\n\tB", val);
283     }
284 
285     @Test
286     public void replaceWithJoran() throws JoranException {
287         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "pattern/replace0.xml");
288         //StatusPrinter.print(lc);
289         root.getAppender("LIST");
290         String msg = "And the number is 4111111111110000, expiring on 12/2010";
291         logger.debug(msg);
292         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
293         assertNotNull(sla);
294         assertEquals(1, sla.strList.size());
295         assertEquals("And the number is XXXX, expiring on 12/2010", sla.strList.get(0));
296     }
297 
298     @Test
299     public void replaceWithJoran_NEWLINE() throws JoranException {
300         loggerContext.putProperty("TAB", "\t");
301         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "pattern/replaceNewline.xml");
302         //StatusPrinter.print(lc);
303         root.getAppender("LIST");
304         String msg = "A\nC";
305         logger.debug(msg);
306         StringListAppender<ILoggingEvent> sla = (StringListAppender<ILoggingEvent>) root.getAppender("LIST");
307         assertNotNull(sla);
308         assertEquals(1, sla.strList.size());
309         assertEquals("A\n\tC", sla.strList.get(0));
310     }
311 
312     @Test
313     public void prefixConverterSmoke() {
314         String pattern = "%prefix(%logger) %message";
315         pl.setPattern(pattern);
316         pl.start();
317         String val = pl.doLayout(makeLoggingEvent("hello", null));
318         assertEquals("logger=" + logger.getName() + " hello", val);
319     }
320 
321     @Test
322     public void prefixConverterWithMDC() {
323         String mdcKey = "boo";
324         String mdcVal = "moo";
325 
326         String pattern = "%prefix(%level %logger %X{" + mdcKey + "}) %message";
327         pl.setPattern(pattern);
328         pl.start();
329         logbackMDCAdapter.put(mdcKey, mdcVal);
330         try {
331             String val = pl.doLayout(makeLoggingEvent("hello", null));
332 
333             assertEquals("level=" + "INFO logger=" + logger.getName() + " " + mdcKey + "=" + mdcVal + " hello", val);
334 
335         } finally {
336             MDC.remove(mdcKey);
337         }
338     }
339 
340     @Test
341     public void prefixConverterWithProperty() {
342 
343         try {
344             String propertyKey = "px1953";
345             String propertyVal = "pxVal";
346 
347             System.setProperty(propertyKey, propertyVal);
348 
349             String pattern = "%prefix(%logger %property{" + propertyKey + "}) %message";
350             pl.setPattern(pattern);
351             pl.start();
352 
353             String val = pl.doLayout(makeLoggingEvent("hello", null));
354 
355             assertEquals("logger=" + logger.getName() + " " + propertyKey + "=" + propertyVal + " hello", val);
356 
357         } finally {
358             System.clearProperty("px");
359         }
360     }
361 
362 }