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.joran;
16  
17  import ch.qos.logback.classic.ClassicTestConstants;
18  import ch.qos.logback.classic.Logger;
19  import ch.qos.logback.classic.LoggerContext;
20  import ch.qos.logback.classic.spi.ILoggingEvent;
21  import ch.qos.logback.classic.util.LogbackMDCAdapter;
22  import ch.qos.logback.core.Appender;
23  import ch.qos.logback.core.FileAppender;
24  import ch.qos.logback.core.joran.spi.JoranException;
25  import ch.qos.logback.core.rolling.RollingFileAppender;
26  import ch.qos.logback.core.status.Status;
27  import ch.qos.logback.core.status.testUtil.StatusChecker;
28  import ch.qos.logback.core.testUtil.RandomUtil;
29  import ch.qos.logback.core.util.StatusPrinter2;
30  import org.junit.jupiter.api.Disabled;
31  import org.junit.jupiter.api.Test;
32  import org.slf4j.spi.MDCAdapter;
33  
34  import static ch.qos.logback.core.model.processor.FileCollisionAnalyser.COLLISION_MESSAGE;
35  import static org.junit.jupiter.api.Assertions.*;
36  
37  public class FileCollisionAnalyserTest {
38  
39      LoggerContext loggerContext = new LoggerContext();
40      MDCAdapter mdcAdapter = new LogbackMDCAdapter();
41      Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
42      StatusPrinter2 statusPrinter2 = new StatusPrinter2();
43      StatusChecker checker = new StatusChecker(loggerContext);
44      int diff = RandomUtil.getPositiveInt();
45  
46      String aLoggerName = "ch.qos.logback";
47      Logger aLogger = loggerContext.getLogger(aLoggerName);
48  
49      String outputTargetVal = ClassicTestConstants.OUTPUT_DIR_PREFIX + "collision/output-" + diff + ".log";
50      String fileNamePatternVal = ClassicTestConstants.OUTPUT_DIR_PREFIX + "collision/output-%d{yyyy-MM-dd}-" + diff + ".log";
51  
52      void configure(String file) throws JoranException {
53          loggerContext.setMDCAdapter(mdcAdapter);
54          JoranConfigurator jc = new JoranConfigurator();
55          jc.setContext(loggerContext);
56          loggerContext.putProperty("outputTargetKey", outputTargetVal);
57          loggerContext.putProperty("fileNamePatternKey", fileNamePatternVal);
58  
59          jc.doConfigure(file);
60  
61      }
62  
63      @Test
64      public void fileCollision() throws JoranException {
65          String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatFile.xml";
66          runCollisionTest(configFile, 1, 0, "file", outputTargetVal);
67      }
68  
69  
70      @Test
71      public void testRollingFileAppenderCollisionByFile() throws JoranException {
72          String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatRollingFileAppenderByFile.xml";
73          runCollisionTest(configFile, 0, 1, "file", outputTargetVal);
74      }
75  
76      @Test
77      public void testRollingFileAppenderCollisionByFilePattern() throws JoranException {
78          String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatRollingFileAppenderByFilePattern.xml";
79          runCollisionTest(configFile, 0,1, "fileNamePattern", fileNamePatternVal);
80      }
81  
82      @Test
83      public void testMixedFileaAppenderRollingFileAppenderCollisionByFile() throws JoranException {
84          String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatMixedFileAndRolling.xml";
85          runCollisionTest(configFile, 1, 0, "file", outputTargetVal);
86      }
87  
88      @Test
89      public void testConditionalFileCollision() throws JoranException {
90          String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/conditionalRepeat.xml";
91          configure(configFile);
92          //statusPrinter2.print(loggerContext);
93  
94          Appender<ILoggingEvent> fileAppender1 = root.getAppender("FILE1");
95          assertNull(fileAppender1);
96  
97          Appender<ILoggingEvent> fileAppender2 = root.getAppender("FILE2");
98          assertNotNull(fileAppender2);
99          checker.assertIsWarningOrErrorFree();
100     }
101 
102 
103     public void runCollisionTest(String configFile, int fileAppenderCount, int rollingAppenderCount, String tagName, String value) throws JoranException {
104         configure(configFile);
105         //statusPrinter2.print(loggerContext);
106 
107         Appender<ILoggingEvent> fileAppender1 = root.getAppender("FILE1");
108         assertNotNull(fileAppender1);
109 
110         Appender<ILoggingEvent> fileAppender2 = aLogger.getAppender("FILE2");
111         assertNull(fileAppender2);
112 
113         //statusPrinter2.print(loggerContext);
114 
115         String expectationPattern = COLLISION_MESSAGE.replace("[", "\\[").replace("]", "\\]");
116 
117         String sanitizeValue = value.replace("{", "\\{").replace("}", "\\}");
118         String expected = String.format(expectationPattern, "FILE2", tagName, sanitizeValue, "FILE1");
119         checker.assertContainsMatch(Status.ERROR, expected);
120         checker.assertMatchCount("About to instantiate appender of type \\[" + FileAppender.class.getName() + "\\]", fileAppenderCount);
121         checker.assertMatchCount("About to instantiate appender of type \\[" + RollingFileAppender.class.getName() + "\\]", rollingAppenderCount);
122     }
123 
124 
125 }