Skip to content

Commit a3eb14d

Browse files
committed
in response to issues/959, collision detection is now done by FileCollisionAnalyser
and not within File*Appender in start() See also #959 Signed-off-by: ceki <[email protected]>
1 parent 681b2be commit a3eb14d

23 files changed

+624
-237
lines changed

logback-classic/src/main/java/ch/qos/logback/classic/joran/ModelClassToModelHandlerLinker.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@
2222
import ch.qos.logback.core.model.AppenderRefModel;
2323
import ch.qos.logback.core.model.InsertFromJNDIModel;
2424
import ch.qos.logback.core.model.ModelHandlerFactoryMethod;
25-
import ch.qos.logback.core.model.processor.AppenderModelHandler;
26-
import ch.qos.logback.core.model.processor.AppenderRefDependencyAnalyser;
27-
import ch.qos.logback.core.model.processor.AppenderRefModelHandler;
28-
import ch.qos.logback.core.model.processor.DefaultProcessor;
29-
import ch.qos.logback.core.model.processor.InsertFromJNDIModelHandler;
30-
import ch.qos.logback.core.model.processor.RefContainerDependencyAnalyser;
25+
import ch.qos.logback.core.model.processor.*;
3126

3227
/**
3328
* For a given DefaultProcessor instance link a {@link ch.qos.logback.core.model.Model Model} class to a
@@ -62,25 +57,25 @@ public void link(DefaultProcessor defaultProcessor) {
6257
defaultProcessor.addHandler(LoggerModel.class, LoggerModelHandler::makeInstance);
6358
defaultProcessor.addHandler(LevelModel.class, LevelModelHandler::makeInstance);
6459

65-
defaultProcessor.addAnalyser(LoggerModel.class,
66-
() -> new RefContainerDependencyAnalyser(context, LoggerModel.class));
67-
6860
defaultProcessor.addAnalyser(RootLoggerModel.class,
69-
() -> new RefContainerDependencyAnalyser(context, RootLoggerModel.class));
61+
() -> new AppenderRefDependencyAnalyser(context));
62+
63+
defaultProcessor.addAnalyser(LoggerModel.class,
64+
() -> new AppenderRefDependencyAnalyser(context));
7065

7166
defaultProcessor.addAnalyser(AppenderModel.class,
72-
() -> new RefContainerDependencyAnalyser(context, AppenderModel.class));
67+
() -> new AppenderRefDependencyAnalyser(context));
7368

74-
defaultProcessor.addAnalyser(AppenderRefModel.class, () -> new AppenderRefDependencyAnalyser(context));
69+
defaultProcessor.addAnalyser(AppenderModel.class, () -> new FileCollisionAnalyser(context));
7570

7671
sealModelFilters(defaultProcessor);
7772

7873
}
7974

8075
public ModelHandlerFactoryMethod getConfigurationModelHandlerFactoryMethod() {
81-
if(configurationModelHandlerFactoryMethod == null) {
76+
if (configurationModelHandlerFactoryMethod == null) {
8277
//System.out.println("returning default ConfigurationModelHandler::makeInstance;");
83-
return ConfigurationModelHandler::makeInstance;
78+
return ConfigurationModelHandler::makeInstance;
8479
} else {
8580
//System.out.println("returning set "+configurationModelHandlerFactoryMethod);
8681
return configurationModelHandlerFactoryMethod;

logback-classic/src/test/input/joran/asyncAppender_list_after.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
<?xml version="1.0" encoding="UTF-8" ?>
2-
<!DOCTYPE configuration>
3-
41
<configuration debug="false">
52

6-
73
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
84
<queueSize>256</queueSize>
95
<includeCallerData>false</includeCallerData>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!--
3+
~ Logback: the reliable, generic, fast and flexible logging framework.
4+
~ Copyright (C) 1999-2025, QOS.ch. All rights reserved.
5+
~
6+
~ This program and the accompanying materials are dual-licensed under
7+
~ either the terms of the Eclipse Public License v1.0 as published by
8+
~ the Eclipse Foundation
9+
~
10+
~ or (per the licensee's choosing)
11+
~
12+
~ under the terms of the GNU Lesser General Public License version 2.1
13+
~ as published by the Free Software Foundation.
14+
-->
15+
16+
<!DOCTYPE configuration>
17+
18+
<configuration debug="false">
19+
20+
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
21+
<import class="ch.qos.logback.core.FileAppender"/>
22+
23+
<appender name="FILE1" class="FileAppender">
24+
<file>${outputTargetKey}</file>
25+
<encoder class="PatternLayoutEncoder">
26+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
27+
</encoder>
28+
</appender>
29+
30+
31+
<appender name="FILE2" class="FileAppender">
32+
<file>${outputTargetKey}</file>
33+
<encoder class="PatternLayoutEncoder">
34+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
35+
</encoder>
36+
</appender>
37+
38+
39+
<logger name="ch.qos.logback" level="INFO">
40+
<appender-ref ref="FILE2"/>
41+
</logger>
42+
43+
<root level="DEBUG">
44+
<appender-ref ref="FILE1"/>
45+
</root>
46+
47+
</configuration>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!DOCTYPE configuration>
2+
3+
<!--
4+
~ Logback: the reliable, generic, fast and flexible logging framework.
5+
~ Copyright (C) 1999-2025, QOS.ch. All rights reserved.
6+
~
7+
~ This program and the accompanying materials are dual-licensed under
8+
~ either the terms of the Eclipse Public License v1.0 as published by
9+
~ the Eclipse Foundation
10+
~
11+
~ or (per the licensee's choosing)
12+
~
13+
~ under the terms of the GNU Lesser General Public License version 2.1
14+
~ as published by the Free Software Foundation.
15+
-->
16+
17+
<configuration debug="false">
18+
19+
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
20+
<import class="ch.qos.logback.core.FileAppender"/>
21+
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
22+
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
23+
24+
<appender name="FILE1" class="FileAppender">
25+
<file>${outputTargetKey}</file>
26+
<encoder class="PatternLayoutEncoder">
27+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
28+
</encoder>
29+
</appender>
30+
31+
32+
<appender name="FILE2" class="RollingFileAppender">
33+
<file>${outputTargetKey}</file>
34+
<rollingPolicy class="TimeBasedRollingPolicy">
35+
<fileNamePattern>${fileNamePatternKey}</fileNamePattern>
36+
<maxHistory>30</maxHistory>
37+
<totalSizeCap>3GB</totalSizeCap>
38+
</rollingPolicy>
39+
<encoder class="PatternLayoutEncoder">
40+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
41+
</encoder>
42+
</appender>
43+
44+
45+
<logger name="ch.qos.logback" level="INFO">
46+
<appender-ref ref="FILE2"/>
47+
</logger>
48+
49+
<root level="DEBUG">
50+
<appender-ref ref="FILE1"/>
51+
</root>
52+
53+
</configuration>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!DOCTYPE configuration>
2+
3+
<!--
4+
~ Logback: the reliable, generic, fast and flexible logging framework.
5+
~ Copyright (C) 1999-2025, QOS.ch. All rights reserved.
6+
~
7+
~ This program and the accompanying materials are dual-licensed under
8+
~ either the terms of the Eclipse Public License v1.0 as published by
9+
~ the Eclipse Foundation
10+
~
11+
~ or (per the licensee's choosing)
12+
~
13+
~ under the terms of the GNU Lesser General Public License version 2.1
14+
~ as published by the Free Software Foundation.
15+
-->
16+
17+
<configuration debug="false">
18+
19+
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
20+
<import class="ch.qos.logback.core.FileAppender"/>
21+
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
22+
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
23+
24+
<appender name="FILE1" class="RollingFileAppender">
25+
<file>${outputTargetKey}</file>
26+
<rollingPolicy class="TimeBasedRollingPolicy">
27+
<fileNamePattern>${fileNamePatternKey}</fileNamePattern>
28+
<maxHistory>30</maxHistory>
29+
<totalSizeCap>3GB</totalSizeCap>
30+
</rollingPolicy>
31+
<encoder class="PatternLayoutEncoder">
32+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
33+
</encoder>
34+
</appender>
35+
36+
37+
<appender name="FILE2" class="RollingFileAppender">
38+
<file>${outputTargetKey}</file>
39+
<rollingPolicy class="TimeBasedRollingPolicy">
40+
<fileNamePattern>DISTINCT${fileNamePatternKey}</fileNamePattern>
41+
<maxHistory>30</maxHistory>
42+
<totalSizeCap>3GB</totalSizeCap>
43+
</rollingPolicy>
44+
<encoder class="PatternLayoutEncoder">
45+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
46+
</encoder>
47+
</appender>
48+
49+
50+
<logger name="ch.qos.logback" level="INFO">
51+
<appender-ref ref="FILE2"/>
52+
</logger>
53+
54+
<root level="DEBUG">
55+
<appender-ref ref="FILE1"/>
56+
</root>
57+
58+
</configuration>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!--
2+
~ Logback: the reliable, generic, fast and flexible logging framework.
3+
~ Copyright (C) 1999-2025, 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+
15+
<!DOCTYPE configuration>
16+
17+
<configuration debug="false">
18+
19+
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
20+
<import class="ch.qos.logback.core.FileAppender"/>
21+
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
22+
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
23+
24+
<appender name="FILE1" class="RollingFileAppender">
25+
<file>${outputTargetKey}</file>
26+
<rollingPolicy class="TimeBasedRollingPolicy">
27+
<fileNamePattern>${fileNamePatternKey}</fileNamePattern>
28+
<maxHistory>30</maxHistory>
29+
<totalSizeCap>3GB</totalSizeCap>
30+
</rollingPolicy>
31+
<encoder class="PatternLayoutEncoder">
32+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
33+
</encoder>
34+
</appender>
35+
36+
37+
38+
<appender name="FILE2" class="RollingFileAppender">
39+
<file>DISTICNT${outputTargetKey}</file>
40+
<rollingPolicy class="TimeBasedRollingPolicy">
41+
<fileNamePattern>${fileNamePatternKey}</fileNamePattern>
42+
<maxHistory>30</maxHistory>
43+
<totalSizeCap>3GB</totalSizeCap>
44+
</rollingPolicy>
45+
<encoder class="PatternLayoutEncoder">
46+
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
47+
</encoder>
48+
</appender>
49+
50+
51+
<logger name="ch.qos.logback" level="INFO">
52+
<appender-ref ref="FILE2"/>
53+
</logger>
54+
55+
<root level="DEBUG">
56+
<appender-ref ref="FILE1"/>
57+
</root>
58+
59+
</configuration>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Logback: the reliable, generic, fast and flexible logging framework.
3+
* Copyright (C) 1999-2025, 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+
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+
89+
public void runCollisionTest(String configFile, int fileAppenderCount, int rollingAppenderCount, String tagName, String value) throws JoranException {
90+
configure(configFile);
91+
statusPrinter2.print(loggerContext);
92+
93+
Appender<ILoggingEvent> fileAppender1 = root.getAppender("FILE1");
94+
assertNotNull(fileAppender1);
95+
96+
Appender<ILoggingEvent> fileAppender2 = aLogger.getAppender("FILE2");
97+
assertNull(fileAppender2);
98+
99+
//statusPrinter2.print(loggerContext);
100+
101+
String expectationPattern = COLLISION_MESSAGE.replace("[", "\\[").replace("]", "\\]");
102+
103+
String sanitizeValue = value.replace("{", "\\{").replace("}", "\\}");
104+
String expected = String.format(expectationPattern, "FILE2", tagName, sanitizeValue, "FILE1");
105+
checker.assertContainsMatch(Status.ERROR, expected);
106+
checker.assertMatchCount("About to instantiate appender of type \\[" + FileAppender.class.getName() + "\\]", fileAppenderCount);
107+
checker.assertMatchCount("About to instantiate appender of type \\[" + RollingFileAppender.class.getName() + "\\]", rollingAppenderCount);
108+
}
109+
110+
111+
}

0 commit comments

Comments
 (0)