Skip to content

Commit 63e4262

Browse files
committed
feat: add support for log4j2
Also do some refactoring along the way.
1 parent cfb8701 commit 63e4262

File tree

16 files changed

+654
-132
lines changed

16 files changed

+654
-132
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ subprojects {
1010

1111
ext {
1212
junitVersion = '5.4.0'
13-
slf4jVersion = '1.7.26'
1413
logbackVersion = '1.2.3'
14+
log4j2Version = '2.11.2'
15+
slf4jVersion = '1.7.26'
1516
}
1617

1718
repositories {

logunit-core/src/main/java/io/github/netmikey/logunit/api/LogCapturer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class LogCapturer implements BeforeTestExecutionCallback, AfterTestExecut
2727
private Level defaultLevel = Level.INFO;
2828

2929
private LogCapturer() {
30-
// Do not instantiate direcly.
30+
// Do not instantiate directly.
3131
}
3232

3333
/**
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.github.netmikey.logunit.core;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import org.slf4j.event.Level;
7+
8+
import io.github.netmikey.logunit.api.LogProvider;
9+
10+
/**
11+
* Base class for log providers, handling the registration of type- and
12+
* name-based logger capturing.
13+
*/
14+
public abstract class BaseLogProvider implements LogProvider {
15+
16+
private final Map<Class<?>, Level> loggerTypes = new HashMap<>();
17+
18+
private final Map<String, Level> loggerNames = new HashMap<>();
19+
20+
@Override
21+
public void provideForType(Class<?> type, Level level) {
22+
if (loggerTypes.containsKey(type)) {
23+
throw new IllegalArgumentException("LogProvider already providing LogEvents for Logger of type "
24+
+ type.getName() + ". Each logger must only be captured once!");
25+
}
26+
loggerTypes.put(type, level);
27+
}
28+
29+
@Override
30+
public void provideForLogger(String name, Level level) {
31+
if (loggerNames.containsKey(name)) {
32+
throw new IllegalArgumentException("LogProvider already providing LogEvents for Logger with name "
33+
+ name + ". Each logger must only be captured once!");
34+
}
35+
loggerNames.put(name, level);
36+
}
37+
38+
/**
39+
* Get the loggerTypes.
40+
*
41+
* @return Returns the loggerTypes.
42+
*/
43+
protected Map<Class<?>, Level> getLoggerTypes() {
44+
return loggerTypes;
45+
}
46+
47+
/**
48+
* Get the loggerNames.
49+
*
50+
* @return Returns the loggerNames.
51+
*/
52+
protected Map<String, Level> getLoggerNames() {
53+
return loggerNames;
54+
}
55+
56+
}

logunit-jul/src/main/java/io/github/netmikey/logunit/jul/JulLogProvider.java

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.github.netmikey.logunit.jul;
22

3-
import java.util.Collections;
43
import java.util.HashMap;
54
import java.util.List;
65
import java.util.Map;
@@ -15,55 +14,22 @@
1514
import org.slf4j.event.LoggingEvent;
1615

1716
import io.github.netmikey.logunit.api.LogCapturer;
18-
import io.github.netmikey.logunit.api.LogProvider;
17+
import io.github.netmikey.logunit.core.BaseLogProvider;
1918

2019
/**
2120
* {@link LogCapturer} implementation based on JUL.
2221
*/
23-
public class JulLogProvider implements LogProvider {
24-
25-
private static final Map<org.slf4j.event.Level, java.util.logging.Level> LEVEL_MAPPING;
26-
27-
private static final Map<java.util.logging.Level, org.slf4j.event.Level> LEVEL_MAPPING_REVERSE;
28-
29-
static {
30-
Map<org.slf4j.event.Level, java.util.logging.Level> levelMapping = new HashMap<>();
31-
levelMapping.put(org.slf4j.event.Level.TRACE, java.util.logging.Level.FINEST);
32-
levelMapping.put(org.slf4j.event.Level.DEBUG, java.util.logging.Level.FINE);
33-
levelMapping.put(org.slf4j.event.Level.INFO, java.util.logging.Level.INFO);
34-
levelMapping.put(org.slf4j.event.Level.WARN, java.util.logging.Level.WARNING);
35-
levelMapping.put(org.slf4j.event.Level.ERROR, java.util.logging.Level.SEVERE);
36-
37-
LEVEL_MAPPING = Collections.unmodifiableMap(levelMapping);
38-
39-
Map<java.util.logging.Level, org.slf4j.event.Level> levelMappingReverse = new HashMap<>();
40-
levelMapping.forEach((key, value) -> levelMappingReverse.put(value, key));
41-
levelMappingReverse.put(java.util.logging.Level.CONFIG, org.slf4j.event.Level.INFO);
42-
levelMappingReverse.put(java.util.logging.Level.FINER, org.slf4j.event.Level.DEBUG);
43-
44-
LEVEL_MAPPING_REVERSE = Collections.unmodifiableMap(levelMappingReverse);
45-
}
22+
public class JulLogProvider extends BaseLogProvider {
4623

4724
private final ListHandler listHandler = new ListHandler();
4825

49-
private final Map<String, Level> loggerNames = new HashMap<>();
50-
5126
private final Map<String, Level> originalLevels = new HashMap<>();
5227

5328
@Override
5429
public void provideForType(Class<?> type, org.slf4j.event.Level level) {
5530
provideForLogger(type.getName(), level);
5631
}
5732

58-
@Override
59-
public void provideForLogger(String name, org.slf4j.event.Level level) {
60-
if (loggerNames.containsKey(name)) {
61-
throw new IllegalArgumentException("LogProvider already providing LogEvents for Logger with name "
62-
+ name + ". Each logger must only be captured once!");
63-
}
64-
loggerNames.put(name, mapLevel(level));
65-
}
66-
6733
@Override
6834
public List<LoggingEvent> getEvents() {
6935
return StreamSupport.stream(listHandler.spliterator(), false)
@@ -84,15 +50,13 @@ public void afterTestExecution(ExtensionContext context) {
8450
}
8551

8652
private void addAppenderToLoggingSources() {
87-
for (Map.Entry<String, Level> logSource : loggerNames.entrySet()) {
88-
addAppenderToLogger(logSource.getKey(), logSource.getValue());
89-
}
53+
getLoggerNames().forEach((loggerName, level) -> {
54+
addAppenderToLogger(loggerName, LevelMapper.mapLevel(level));
55+
});
9056
}
9157

9258
private void detachAppenderFromLoggingSources() {
93-
for (Map.Entry<String, Level> logSource : loggerNames.entrySet()) {
94-
detachAppenderFromLogger(logSource.getKey());
95-
}
59+
getLoggerNames().keySet().forEach(this::detachAppenderFromLogger);
9660
}
9761

9862
private void addAppenderToLogger(String name, Level level) {
@@ -152,7 +116,7 @@ public String getLoggerName() {
152116

153117
@Override
154118
public org.slf4j.event.Level getLevel() {
155-
return mapLevel(record.getLevel());
119+
return LevelMapper.mapLevel(record.getLevel());
156120
}
157121

158122
@Override
@@ -161,20 +125,4 @@ public Object[] getArgumentArray() {
161125
}
162126
};
163127
}
164-
165-
private Level mapLevel(org.slf4j.event.Level level) {
166-
Level result = LEVEL_MAPPING.get(level);
167-
if (result == null) {
168-
throw new IllegalArgumentException("Cannot map log level " + level + " to a JUL log level");
169-
}
170-
return result;
171-
}
172-
173-
private org.slf4j.event.Level mapLevel(Level level) {
174-
org.slf4j.event.Level result = LEVEL_MAPPING_REVERSE.get(level);
175-
if (result == null) {
176-
throw new IllegalArgumentException("Cannot map JUL log level " + level + " to an slf4j log level");
177-
}
178-
return result;
179-
}
180128
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.github.netmikey.logunit.jul;
2+
3+
import java.util.Collections;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
import java.util.logging.Level;
7+
8+
/**
9+
* Utility class to map between slf4j and logging implementation levels.
10+
*/
11+
public class LevelMapper {
12+
13+
private static final Map<org.slf4j.event.Level, java.util.logging.Level> LEVEL_MAPPING;
14+
15+
private static final Map<java.util.logging.Level, org.slf4j.event.Level> LEVEL_MAPPING_REVERSE;
16+
17+
static {
18+
Map<org.slf4j.event.Level, java.util.logging.Level> levelMapping = new HashMap<>();
19+
levelMapping.put(org.slf4j.event.Level.TRACE, java.util.logging.Level.FINEST);
20+
levelMapping.put(org.slf4j.event.Level.DEBUG, java.util.logging.Level.FINE);
21+
levelMapping.put(org.slf4j.event.Level.INFO, java.util.logging.Level.INFO);
22+
levelMapping.put(org.slf4j.event.Level.WARN, java.util.logging.Level.WARNING);
23+
levelMapping.put(org.slf4j.event.Level.ERROR, java.util.logging.Level.SEVERE);
24+
25+
LEVEL_MAPPING = Collections.unmodifiableMap(levelMapping);
26+
27+
Map<java.util.logging.Level, org.slf4j.event.Level> levelMappingReverse = new HashMap<>();
28+
levelMapping.forEach((key, value) -> levelMappingReverse.put(value, key));
29+
levelMappingReverse.put(java.util.logging.Level.CONFIG, org.slf4j.event.Level.INFO);
30+
levelMappingReverse.put(java.util.logging.Level.FINER, org.slf4j.event.Level.DEBUG);
31+
32+
LEVEL_MAPPING_REVERSE = Collections.unmodifiableMap(levelMappingReverse);
33+
}
34+
35+
/**
36+
* Map the specified Slf4j level to the appropriate implementation's level.
37+
*
38+
* @param level
39+
* The slf4j level.
40+
* @return The implementation's level.
41+
*/
42+
public static Level mapLevel(org.slf4j.event.Level level) {
43+
Level result = LEVEL_MAPPING.get(level);
44+
if (result == null) {
45+
throw new IllegalArgumentException("Cannot map log level " + level + " to a JUL log level");
46+
}
47+
return result;
48+
}
49+
50+
/**
51+
* Map the specified implementation level to the appropriate Slf4j level.
52+
*
53+
* @param level
54+
* The logging implementation's level.
55+
* @return The slf4j level.
56+
*/
57+
public static org.slf4j.event.Level mapLevel(Level level) {
58+
org.slf4j.event.Level result = LEVEL_MAPPING_REVERSE.get(level);
59+
if (result == null) {
60+
throw new IllegalArgumentException("Cannot map JUL log level " + level + " to an slf4j log level");
61+
}
62+
return result;
63+
}
64+
65+
}

logunit-log4j2/build.gradle

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
description = "LogUnit's Log4j2 implementation."
3+
4+
apply from: new File(rootProject.projectDir, 'publishing-build.gradle')
5+
6+
dependencies {
7+
api project(':logunit-core')
8+
9+
implementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
10+
implementation("org.apache.logging.log4j:log4j-core:${log4j2Version}")
11+
12+
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
13+
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:${log4j2Version}")
14+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package io.github.netmikey.logunit.log4j2;
2+
3+
import java.util.Collections;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
7+
import org.apache.logging.log4j.Level;
8+
9+
/**
10+
* Utility class to map between slf4j and logging implementation levels.
11+
*/
12+
public class LevelMapper {
13+
14+
private static final Map<org.slf4j.event.Level, org.apache.logging.log4j.Level> LEVEL_MAPPING;
15+
16+
private static final Map<org.apache.logging.log4j.Level, org.slf4j.event.Level> LEVEL_MAPPING_REVERSE;
17+
18+
static {
19+
Map<org.slf4j.event.Level, org.apache.logging.log4j.Level> levelMapping = new HashMap<>();
20+
levelMapping.put(org.slf4j.event.Level.TRACE, org.apache.logging.log4j.Level.TRACE);
21+
levelMapping.put(org.slf4j.event.Level.DEBUG, org.apache.logging.log4j.Level.DEBUG);
22+
levelMapping.put(org.slf4j.event.Level.INFO, org.apache.logging.log4j.Level.INFO);
23+
levelMapping.put(org.slf4j.event.Level.WARN, org.apache.logging.log4j.Level.WARN);
24+
levelMapping.put(org.slf4j.event.Level.ERROR, org.apache.logging.log4j.Level.ERROR);
25+
26+
LEVEL_MAPPING = Collections.unmodifiableMap(levelMapping);
27+
28+
Map<org.apache.logging.log4j.Level, org.slf4j.event.Level> levelMappingReverse = new HashMap<>();
29+
levelMapping.forEach((key, value) -> levelMappingReverse.put(value, key));
30+
levelMappingReverse.put(org.apache.logging.log4j.Level.ALL, org.slf4j.event.Level.TRACE);
31+
levelMappingReverse.put(org.apache.logging.log4j.Level.FATAL, org.slf4j.event.Level.ERROR);
32+
levelMappingReverse.put(org.apache.logging.log4j.Level.OFF, org.slf4j.event.Level.ERROR);
33+
34+
LEVEL_MAPPING_REVERSE = Collections.unmodifiableMap(levelMappingReverse);
35+
}
36+
37+
/**
38+
* Map the specified Slf4j level to the appropriate implementation's level.
39+
*
40+
* @param level
41+
* The slf4j level.
42+
* @return The implementation's level.
43+
*/
44+
public static Level mapLevel(org.slf4j.event.Level level) {
45+
Level result = LEVEL_MAPPING.get(level);
46+
if (result == null) {
47+
throw new IllegalArgumentException("Cannot map log level " + level + " to a log4j2 log level");
48+
}
49+
return result;
50+
}
51+
52+
/**
53+
* Map the specified implementation level to the appropriate Slf4j level.
54+
*
55+
* @param level
56+
* The logging implementation's level.
57+
* @return The slf4j level.
58+
*/
59+
public static org.slf4j.event.Level mapLevel(Level level) {
60+
org.slf4j.event.Level result = LEVEL_MAPPING_REVERSE.get(level);
61+
if (result == null) {
62+
throw new IllegalArgumentException("Cannot map log4j2 log level " + level + " to an slf4j log level");
63+
}
64+
return result;
65+
}
66+
67+
}

0 commit comments

Comments
 (0)