1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, 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 v2.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  
15  package ch.qos.logback.classic.blackbox.joran;
16  
17  import ch.qos.logback.classic.ClassicConstants;
18  import ch.qos.logback.classic.Level;
19  import ch.qos.logback.classic.Logger;
20  import ch.qos.logback.classic.LoggerContext;
21  import ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants;
22  import ch.qos.logback.classic.joran.JoranConfigurator;
23  import ch.qos.logback.classic.jul.JULHelper;
24  import ch.qos.logback.classic.spi.ILoggingEvent;
25  import ch.qos.logback.classic.util.DefaultJoranConfigurator;
26  import ch.qos.logback.classic.util.LogbackMDCAdapter;
27  import ch.qos.logback.core.Appender;
28  import ch.qos.logback.core.joran.spi.JoranException;
29  import ch.qos.logback.core.read.ListAppender;
30  import ch.qos.logback.core.testUtil.RandomUtil;
31  import ch.qos.logback.core.testUtil.StringListAppender;
32  import ch.qos.logback.core.util.Loader;
33  import ch.qos.logback.core.util.StatusPrinter;
34  import org.junit.jupiter.api.Disabled;
35  import org.junit.jupiter.api.Test;
36  
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.net.URL;
40  
41  import static org.junit.jupiter.api.Assertions.*;
42  
43  public class BlackboxJoranConfiguratorTest {
44  
45      LoggerContext loggerContext = new LoggerContext();
46      LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter();
47      Logger logger = loggerContext.getLogger(this.getClass().getName());
48      Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
49      StatusChecker checker = new StatusChecker(loggerContext);
50      int diff = RandomUtil.getPositiveInt();
51  
52      void configure(String file) throws JoranException {
53          loggerContext.setMDCAdapter(logbackMDCAdapter);
54          JoranConfigurator jc = new JoranConfigurator();
55          jc.setContext(loggerContext);
56          loggerContext.putProperty("diff", "" + diff);
57          jc.doConfigure(file);
58      }
59  
60  
61      @Test
62      public void eval() throws JoranException {
63          configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "callerData.xml");
64          String msg = "hello world";
65          logger.debug("toto");
66          logger.debug(msg);
67  
68          StringListAppender<ILoggingEvent> slAppender = (StringListAppender<ILoggingEvent>) loggerContext
69                  .getLogger("root").getAppender("STR_LIST");
70          assertNotNull(slAppender);
71          assertEquals(2, slAppender.strList.size());
72          assertTrue(slAppender.strList.get(0).contains(" DEBUG - toto"));
73  
74          String str1 = slAppender.strList.get(1);
75          assertTrue(str1.contains("Caller+0"));
76          assertTrue(str1.contains(" DEBUG - hello world"));
77      }
78  
79      @Disabled
80      @Test
81      public void testEvaluatorFilter() throws JoranException {
82          configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilter.xml");
83  
84          // StatusPrinter.print(loggerContext);
85  
86          logger.warn("hello");
87          logger.error("to be ignored");
88  
89          ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
90  
91          assertNotNull(listAppender);
92          assertEquals(1, listAppender.list.size());
93          ILoggingEvent back = listAppender.list.get(0);
94          assertEquals(Level.WARN, back.getLevel());
95          assertEquals("hello", back.getMessage());
96      }
97  
98      @Disabled
99      @Test
100     public void testEvaluatorFilterWithImports() throws JoranException {
101         configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilterWithImports.xml");
102 
103         // StatusPrinter.print(loggerContext);
104 
105         logger.warn("hello");
106         logger.error("to be ignored");
107 
108         ListAppender<ILoggingEvent> listAppender = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
109 
110         assertNotNull(listAppender);
111         assertEquals(1, listAppender.list.size());
112         ILoggingEvent back = listAppender.list.get(0);
113         assertEquals(Level.WARN, back.getLevel());
114         assertEquals("hello", back.getMessage());
115     }
116 
117     @Test
118     public void conditional1673() throws JoranException {
119         loggerContext.putProperty("EXTRA", "true");
120         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673.xml";
121         configure(configFileAsStr);
122     }
123 
124     @Test
125     public void conditional1673bisWithActiveThen() throws JoranException {
126         loggerContext.putProperty("EXTRA", "true");
127         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673bis.xml";
128         configure(configFileAsStr);
129         Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
130         ListAppender<ILoggingEvent> listThen = (ListAppender<ILoggingEvent>) root.getAppender("LIST_THEN");
131         assertNotNull(listThen);
132 
133         ListAppender<ILoggingEvent> listElse = (ListAppender<ILoggingEvent>) root.getAppender("LIST_ELSE");
134         assertNull(listElse);
135     }
136 
137     // See also https://github.com/qos-ch/logback/issues/1016
138     @Test
139     public void conditionalWithAppenderInclusion() throws JoranException {
140         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/topConditionalWithAppenderInclusion.xml";
141         configure(configFileAsStr);
142         Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
143         Appender<ILoggingEvent> appender = root.getAppender("MISSING");
144         assertNull(appender);
145 
146         ListAppender<ILoggingEvent> listElse = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
147         assertNotNull(listElse);
148     }
149 
150     @Test
151     public void conditional1673bisWithActiveElse() throws JoranException {
152         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673bis.xml";
153         configure(configFileAsStr);
154         Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
155         ListAppender<ILoggingEvent> listThen = (ListAppender<ILoggingEvent>) root.getAppender("LIST_THEN");
156         assertNull(listThen);
157 
158         ListAppender<ILoggingEvent> listElse = (ListAppender<ILoggingEvent>) root.getAppender("LIST_ELSE");
159         assertNotNull(listElse);
160     }
161 
162     @Test
163     public void nestedIf() throws JoranException {
164         loggerContext.putProperty("EXTRA", "true");
165         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1678.xml";
166         configure(configFileAsStr);
167         StatusPrinter.print(loggerContext);
168 
169     }
170 
171     @Test
172     public void levelChangePropagator0() throws JoranException, IOException, InterruptedException {
173         String loggerName = "changePropagator0" + diff;
174         java.util.logging.Logger.getLogger(loggerName).setLevel(java.util.logging.Level.INFO);
175         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator0.xml";
176         configure(configFileAsStr);
177 
178         checker.assertIsErrorFree();
179         verifyJULLevel(loggerName, null);
180         verifyJULLevel("a.b.c." + diff, Level.WARN);
181         verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE);
182     }
183 
184     @Test
185     public void levelChangePropagator1() throws JoranException, IOException, InterruptedException {
186         String loggerName = "changePropagator1" + diff;
187         java.util.logging.Logger logger1 = java.util.logging.Logger.getLogger(loggerName);
188         logger1.setLevel(java.util.logging.Level.INFO);
189         verifyJULLevel(loggerName, Level.INFO);
190         String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator1.xml";
191         configure(configFileAsStr);
192 
193         checker.assertIsErrorFree();
194         verifyJULLevel(loggerName, Level.INFO); //
195         verifyJULLevel("a.b.c." + diff, Level.WARN);
196         verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE);
197     }
198 
199     void verifyJULLevel(String loggerName, Level expectedLevel) {
200         java.util.logging.Logger julLogger = JULHelper.asJULLogger(loggerName);
201         java.util.logging.Level julLevel = julLogger.getLevel();
202 
203         if (expectedLevel == null) {
204             assertNull(julLevel);
205         } else {
206             assertEquals(JULHelper.asJULLevel(expectedLevel), julLevel);
207         }
208     }
209 
210     // See https://github.com/qos-ch/logback/issues/1001
211     // See https://github.com/qos-ch/logback/issues/997
212     @Test
213     public void fileAsResource() throws JoranException, IOException, InterruptedException {
214         JoranConfigurator joranConfigurator = new JoranConfigurator();
215         joranConfigurator.setContext(loggerContext);
216         ClassLoader classLoader = Loader.getClassLoaderOfObject(joranConfigurator);
217 
218 
219         String logbackConfigFile = "asResource/topFile.xml";
220         // asResource/topFile.xml
221         //<configuration xscan="true" scanPeriod="50 millisecond">
222         //        <include resource="asResource/inner1.xml"/>
223         //</configuration>
224 
225         // inner1.xml
226         //  <included>
227         //        <root level="ERROR"/>
228         //  </included>
229 
230         URL aURL = Loader.getResource(logbackConfigFile, classLoader);
231         InputStream inputStream = aURL.openStream();
232         assertNotNull(inputStream);
233         joranConfigurator.doConfigure(inputStream);
234 
235         assertEquals(Level.ERROR, root.getLevel());
236 
237         //StatusPrinter.print(loggerContext);
238         inputStream.close();
239         checker.assertContainsMatch("Scan attribute not set or set to unrecognized value.");
240         checker.assertIsWarningOrErrorFree();
241     }
242 
243 }