Skip to content

Commit 3e728a2

Browse files
committed
Port log4j-to-slf4j changes from 2.x
1 parent 9651905 commit 3e728a2

File tree

15 files changed

+370
-122
lines changed

15 files changed

+370
-122
lines changed

log4j-to-slf4j/pom.xml

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@
3434
<!--
3535
~ OSGi and JPMS options
3636
-->
37-
<slf4j.support.bound>3</slf4j.support.bound>
37+
<slf4j.support.range>[1.7,3)</slf4j.support.range>
3838
<bnd-extra-package-options>
3939
<!-- This bridge also support SLF4J 2.x -->
40-
org.slf4j.*;version="${range;[==,${slf4j.support.bound})}"
40+
org.slf4j.*;version="${slf4j.support.range}"
4141
</bnd-extra-package-options>
42+
<bnd-extra-module-options>
43+
<!-- The module descriptor is in `META-INF/versions/9`
44+
BND 6.x can not find it -->
45+
org.slf4j;substitute="slf4j-api"
46+
</bnd-extra-module-options>
4247
</properties>
4348
<dependencies>
4449
<dependency>
@@ -90,6 +95,36 @@
9095
<type>test-jar</type>
9196
<scope>test</scope>
9297
</dependency>
98+
<dependency>
99+
<groupId>org.apache.logging.log4j</groupId>
100+
<artifactId>log4j-api-test</artifactId>
101+
<scope>test</scope>
102+
</dependency>
93103
</dependencies>
94104

105+
<build>
106+
<plugins>
107+
<!--
108+
~ Unban Logback.
109+
-->
110+
<plugin>
111+
<groupId>org.apache.maven.plugins</groupId>
112+
<artifactId>maven-enforcer-plugin</artifactId>
113+
<executions>
114+
<execution>
115+
<id>ban-logging-dependencies</id>
116+
<configuration>
117+
<rules>
118+
<bannedDependencies>
119+
<includes>
120+
<include>ch.qos.logback:*:*:*:test</include>
121+
</includes>
122+
</bannedDependencies>
123+
</rules>
124+
</configuration>
125+
</execution>
126+
</executions>
127+
</plugin>
128+
</plugins>
129+
</build>
95130
</project>

log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLogger.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@
2929
import org.slf4j.MarkerFactory;
3030
import org.slf4j.spi.LocationAwareLogger;
3131

32-
/**
33-
*
34-
*/
3532
public class SLF4JLogger extends AbstractLogger {
3633

3734
/**

log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContext.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
import org.apache.logging.log4j.spi.LoggerRegistry;
2323
import org.slf4j.LoggerFactory;
2424

25-
/**
26-
*
27-
*/
2825
public class SLF4JLoggerContext implements LoggerContext {
2926
private final LoggerRegistry<ExtendedLogger> loggerRegistry = new LoggerRegistry<>();
3027

log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContextFactory.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
import org.apache.logging.log4j.status.StatusLogger;
2323
import org.apache.logging.log4j.util.LoaderUtil;
2424

25-
/**
26-
*
27-
*/
2825
public class SLF4JLoggerContextFactory implements LoggerContextFactory {
2926
private static final StatusLogger LOGGER = StatusLogger.getLogger();
3027
private static final LoggerContext context = new SLF4JLoggerContext();

log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,13 @@
2222
import ch.qos.logback.core.testUtil.StringListAppender;
2323
import java.util.List;
2424
import org.apache.logging.log4j.LogManager;
25-
import org.junit.ClassRule;
26-
import org.junit.Test;
25+
import org.apache.logging.log4j.test.junit.UsingStatusListener;
26+
import org.junit.jupiter.api.Test;
2727

28+
@UsingStatusListener
29+
@LoggerContextSource
2830
public class CallerInformationTest {
2931

30-
private static final String CONFIG = "target/test-classes/logback-calling-class.xml";
31-
32-
@ClassRule
33-
public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
34-
3532
@Test
3633
public void testClassLogger() throws Exception {
3734
final SLF4JLogger logger = (SLF4JLogger) LogManager.getLogger("ClassLogger");

log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/Log4j2Jira1688Test.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
*/
1717
package org.apache.logging.slf4j;
1818

19+
import static org.assertj.core.api.Assertions.assertThat;
20+
1921
import java.util.Arrays;
20-
import org.junit.Assert;
21-
import org.junit.Test;
22+
import org.junit.jupiter.api.Test;
2223
import org.slf4j.Logger;
2324
import org.slf4j.LoggerFactory;
2425

@@ -44,14 +45,13 @@ public void testLog4j2() {
4445
// First logging of args
4546
logger.error(someFormat, args); // Only the first element (args[0]) of args will be logged - why?
4647
// GG: because the pattern {} picks up the 1 st argument, not the whole array
47-
Assert.assertArrayEquals(originalArgs, args);
48-
System.out.println("args are still ok: " + Arrays.toString(args));
48+
assertThat(args).containsExactly(originalArgs);
4949

5050
// Bug: The second logging of args sets all elements of args to null
5151
logger.error(someFormat, args);
5252
// GG: All is well args is still intact
5353
System.out.println("args " + Arrays.toString(args));
54-
Assert.assertArrayEquals(originalArgs, args);
54+
assertThat(args).containsExactly(originalArgs);
5555
}
5656

5757
/**

log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/LogBuilderTest.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020

2121
import ch.qos.logback.classic.LoggerContext;
22-
import ch.qos.logback.classic.joran.JoranConfigurator;
2322
import ch.qos.logback.classic.spi.ILoggingEvent;
2423
import ch.qos.logback.core.testUtil.StringListAppender;
2524
import java.util.function.Consumer;
@@ -31,31 +30,29 @@
3130
import org.apache.logging.log4j.Logger;
3231
import org.apache.logging.log4j.message.Message;
3332
import org.apache.logging.log4j.message.SimpleMessage;
33+
import org.apache.logging.log4j.test.junit.UsingStatusListener;
3434
import org.junit.jupiter.api.BeforeAll;
3535
import org.junit.jupiter.params.ParameterizedTest;
3636
import org.junit.jupiter.params.provider.MethodSource;
37-
import org.slf4j.LoggerFactory;
3837

38+
@UsingStatusListener
39+
@LoggerContextSource
3940
public class LogBuilderTest {
4041

41-
private static final String CONFIG = "target/test-classes/logback-turbofilter.xml";
4242
private static final CharSequence CHAR_SEQUENCE = "CharSequence";
4343
private static final String STRING = "String";
4444
private static final Message MESSAGE = new SimpleMessage();
4545
private static final Object[] P = {"p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9"};
46-
private static final Object OBJECT = "Object";
46+
private static Object OBJECT = "Object";
4747

48-
private static LoggerContext context;
48+
// Log4j objects
4949
private static Logger logger;
50+
// Logback objects
51+
private static LoggerContext context;
5052
private static StringListAppender<ILoggingEvent> list;
5153

5254
@BeforeAll
5355
public static void setUp() throws Exception {
54-
context = (LoggerContext) LoggerFactory.getILoggerFactory();
55-
final JoranConfigurator configurator = new JoranConfigurator();
56-
configurator.setContext(context);
57-
configurator.doConfigure(CONFIG);
58-
5956
final org.slf4j.Logger slf4jLogger = context.getLogger(LogBuilderTest.class);
6057
logger = LogManager.getLogger(LogBuilderTest.class);
6158
assertThat(slf4jLogger).isSameAs(((SLF4JLogger) logger).getLogger());
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.slf4j;
18+
19+
import ch.qos.logback.classic.Logger;
20+
import ch.qos.logback.classic.LoggerContext;
21+
import ch.qos.logback.classic.joran.JoranConfigurator;
22+
import ch.qos.logback.core.joran.spi.JoranException;
23+
import java.net.URL;
24+
import org.apache.logging.log4j.test.junit.ExtensionContextAnchor;
25+
import org.junit.jupiter.api.extension.BeforeAllCallback;
26+
import org.junit.jupiter.api.extension.BeforeEachCallback;
27+
import org.junit.jupiter.api.extension.ExtensionContext;
28+
import org.junit.jupiter.api.extension.ExtensionContext.Store;
29+
import org.junit.jupiter.api.extension.ExtensionContextException;
30+
import org.junit.jupiter.api.extension.ParameterContext;
31+
import org.junit.jupiter.api.extension.ParameterResolutionException;
32+
import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver;
33+
import org.junit.platform.commons.support.AnnotationSupport;
34+
import org.junit.platform.commons.support.HierarchyTraversalMode;
35+
import org.junit.platform.commons.support.ModifierSupport;
36+
import org.junit.platform.commons.support.ReflectionSupport;
37+
import org.slf4j.LoggerFactory;
38+
39+
class LoggerContextResolver extends TypeBasedParameterResolver<LoggerContext>
40+
implements BeforeAllCallback, BeforeEachCallback {
41+
42+
private static final Object KEY = LoggerContextHolder.class;
43+
44+
@Override
45+
public void beforeEach(ExtensionContext extensionContext) throws Exception {
46+
final Class<?> testClass = extensionContext.getRequiredTestClass();
47+
if (AnnotationSupport.isAnnotated(testClass, LoggerContextSource.class)) {
48+
final LoggerContextHolder holder =
49+
ExtensionContextAnchor.getAttribute(KEY, LoggerContextHolder.class, extensionContext);
50+
if (holder == null) {
51+
throw new IllegalStateException(
52+
"Specified @LoggerContextSource but no LoggerContext found for test class "
53+
+ testClass.getCanonicalName());
54+
}
55+
}
56+
AnnotationSupport.findAnnotation(extensionContext.getRequiredTestMethod(), LoggerContextSource.class)
57+
.ifPresent(source -> {
58+
final LoggerContextHolder holder = new LoggerContextHolder(source, extensionContext);
59+
ExtensionContextAnchor.setAttribute(KEY, holder, extensionContext);
60+
});
61+
final LoggerContextHolder holder =
62+
ExtensionContextAnchor.getAttribute(KEY, LoggerContextHolder.class, extensionContext);
63+
if (holder != null) {
64+
ReflectionSupport.findFields(
65+
extensionContext.getRequiredTestClass(),
66+
f -> ModifierSupport.isNotStatic(f) && f.getType().equals(LoggerContext.class),
67+
HierarchyTraversalMode.TOP_DOWN)
68+
.forEach(f -> {
69+
try {
70+
f.setAccessible(true);
71+
f.set(extensionContext.getRequiredTestInstance(), holder.getLoggerContext());
72+
} catch (ReflectiveOperationException e) {
73+
throw new ExtensionContextException("Failed to inject field " + f, e);
74+
}
75+
});
76+
}
77+
}
78+
79+
@Override
80+
public void beforeAll(ExtensionContext extensionContext) throws Exception {
81+
final Class<?> testClass = extensionContext.getRequiredTestClass();
82+
AnnotationSupport.findAnnotation(testClass, LoggerContextSource.class).ifPresent(testSource -> {
83+
final LoggerContextHolder holder = new LoggerContextHolder(testSource, extensionContext);
84+
ExtensionContextAnchor.setAttribute(KEY, holder, extensionContext);
85+
});
86+
final LoggerContextHolder holder =
87+
ExtensionContextAnchor.getAttribute(KEY, LoggerContextHolder.class, extensionContext);
88+
if (holder != null) {
89+
ReflectionSupport.findFields(
90+
extensionContext.getRequiredTestClass(),
91+
f -> ModifierSupport.isStatic(f) && f.getType().equals(LoggerContext.class),
92+
HierarchyTraversalMode.TOP_DOWN)
93+
.forEach(f -> {
94+
try {
95+
f.setAccessible(true);
96+
f.set(null, holder.getLoggerContext());
97+
} catch (ReflectiveOperationException e) {
98+
throw new ExtensionContextException("Failed to inject field " + f, e);
99+
}
100+
});
101+
}
102+
}
103+
104+
@Override
105+
public LoggerContext resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
106+
throws ParameterResolutionException {
107+
return ExtensionContextAnchor.getAttribute(KEY, LoggerContextHolder.class, extensionContext)
108+
.getLoggerContext();
109+
}
110+
111+
static final class LoggerContextHolder implements Store.CloseableResource {
112+
113+
private final LoggerContext context;
114+
private final Logger logger;
115+
116+
private LoggerContextHolder(final LoggerContextSource source, final ExtensionContext extensionContext) {
117+
this.context = (LoggerContext) LoggerFactory.getILoggerFactory();
118+
Class<?> clazz = extensionContext.getRequiredTestClass();
119+
this.logger = context.getLogger(clazz);
120+
121+
final JoranConfigurator configurator = new JoranConfigurator();
122+
final URL configLocation = getConfigLocation(source, extensionContext);
123+
configurator.setContext(context);
124+
try {
125+
configurator.doConfigure(configLocation);
126+
} catch (final JoranException e) {
127+
throw new ExtensionContextException("Failed to initialize Logback logger context for " + clazz, e);
128+
}
129+
}
130+
131+
private static URL getConfigLocation(
132+
final LoggerContextSource source, final ExtensionContext extensionContext) {
133+
final String value = source.value();
134+
Class<?> clazz = extensionContext.getRequiredTestClass();
135+
URL url = null;
136+
if (value.isEmpty()) {
137+
while (clazz != null) {
138+
url = clazz.getResource(clazz.getSimpleName() + ".xml");
139+
if (url != null) {
140+
break;
141+
}
142+
clazz = clazz.getSuperclass();
143+
}
144+
} else {
145+
url = clazz.getClassLoader().getResource(value);
146+
}
147+
if (url != null) {
148+
return url;
149+
}
150+
throw new ExtensionContextException("Failed to find a default configuration for " + clazz);
151+
}
152+
153+
public LoggerContext getLoggerContext() {
154+
return context;
155+
}
156+
157+
public Logger getLogger() {
158+
return logger;
159+
}
160+
161+
@Override
162+
public void close() {
163+
context.stop();
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)