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