Skip to content

Commit 94c813c

Browse files
committed
Make StatusLogger self-contained and testable
1 parent d7febdd commit 94c813c

32 files changed

+883
-664
lines changed

log4j-api-test/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@
138138
<dependency>
139139
<groupId>org.mockito</groupId>
140140
<artifactId>mockito-core</artifactId>
141-
<scope>test</scope>
142141
</dependency>
143142
<dependency>
144143
<groupId>org.mockito</groupId>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343
import org.junit.platform.commons.support.ModifierSupport;
4444
import org.junit.platform.commons.support.ReflectionSupport;
4545

46-
class StatusLoggerExtension extends TypeBasedParameterResolver<ListStatusListener>
46+
class StatusListenerExtension extends TypeBasedParameterResolver<ListStatusListener>
4747
implements BeforeAllCallback, BeforeEachCallback, TestExecutionExceptionHandler {
4848

4949
private static final Object KEY = ListStatusListener.class;
5050

51-
public StatusLoggerExtension() {
51+
public StatusListenerExtension() {
5252
super(ListStatusListener.class);
5353
}
5454

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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.log4j.test.junit;
18+
19+
import static org.apache.logging.log4j.test.junit.ExtensionContextAnchor.getAttribute;
20+
import static org.apache.logging.log4j.test.junit.ExtensionContextAnchor.setAttribute;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.reset;
23+
24+
import org.apache.logging.log4j.status.StatusLogger;
25+
import org.junit.jupiter.api.extension.AfterAllCallback;
26+
import org.junit.jupiter.api.extension.BeforeAllCallback;
27+
import org.junit.jupiter.api.extension.BeforeEachCallback;
28+
import org.junit.jupiter.api.extension.ExtensionContext;
29+
30+
/**
31+
* Replaces {@link StatusLogger} static instance with a mocked one.
32+
* <p>
33+
* <b>Warning!</b>
34+
* Many classes store the result of {@link StatusLogger#getLogger()} in {@code static} field.
35+
* Hence, the mock replacement must be performed before anybody tries to access it.
36+
* Similarly, we cannot replace the mock in between tests, since it is already stored in {@code static} fields.
37+
* That is why we only reset the mocked instance before each test.
38+
* </p>
39+
*/
40+
class StatusLoggerMockExtension implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback {
41+
42+
private static final String KEY_PREFIX = StatusLoggerMockExtension.class.getSimpleName() + '.';
43+
44+
private static final String INITIAL_STATUS_LOGGER_KEY = KEY_PREFIX + "initialStatusLogger";
45+
46+
@Override
47+
public void beforeAll(final ExtensionContext context) throws Exception {
48+
setAttribute(INITIAL_STATUS_LOGGER_KEY, StatusLogger.getLogger(), context);
49+
final StatusLogger statusLogger = mock(StatusLogger.class);
50+
StatusLogger.setLogger(statusLogger);
51+
}
52+
53+
@Override
54+
public void beforeEach(final ExtensionContext context) throws Exception {
55+
reset(StatusLogger.getLogger());
56+
}
57+
58+
@Override
59+
public void afterAll(final ExtensionContext context) {
60+
final StatusLogger statusLogger = getAttribute(INITIAL_STATUS_LOGGER_KEY, StatusLogger.class, context);
61+
StatusLogger.setLogger(statusLogger);
62+
}
63+
}

log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@
3737
@Documented
3838
@ExtendWith(ExtensionContextAnchor.class)
3939
@ExtendWith(TestPropertyResolver.class)
40-
@ExtendWith(StatusLoggerExtension.class)
40+
@ExtendWith(StatusListenerExtension.class)
4141
public @interface UsingStatusListener {}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.log4j.test.junit;
18+
19+
import static java.lang.annotation.ElementType.METHOD;
20+
import static java.lang.annotation.ElementType.TYPE;
21+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
22+
23+
import java.lang.annotation.Documented;
24+
import java.lang.annotation.Retention;
25+
import java.lang.annotation.Target;
26+
import org.junit.jupiter.api.extension.ExtendWith;
27+
28+
/**
29+
* Shortcut to {@link StatusLoggerMockExtension}.
30+
*/
31+
@Retention(RUNTIME)
32+
@Target({TYPE, METHOD})
33+
@Documented
34+
@ExtendWith(StatusLoggerMockExtension.class)
35+
public @interface UsingStatusLoggerMock {}

log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the license.
1616
*/
1717
@Export
18-
@Version("2.21.1")
18+
@Version("2.22.0")
1919
package org.apache.logging.log4j.test.junit;
2020

2121
import org.osgi.annotation.bundle.Export;

log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
import org.junit.jupiter.api.Test;
4343
import org.junit.jupiter.api.parallel.ResourceAccessMode;
4444
import org.junit.jupiter.api.parallel.ResourceLock;
45+
import org.junitpioneer.jupiter.SetSystemProperty;
4546

4647
@StatusLoggerLevel("WARN")
4748
@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
49+
@SetSystemProperty(key = StatusLogger.BUFFER_CAPACITY_PROPERTY_NAME, value = "200")
4850
public class AbstractLoggerTest {
4951

5052
private static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");

log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@
1919
import java.io.ByteArrayOutputStream;
2020
import java.io.PrintStream;
2121
import org.apache.logging.log4j.Level;
22-
import org.apache.logging.log4j.LogBuilder;
2322
import org.apache.logging.log4j.message.Message;
2423
import org.apache.logging.log4j.message.MessageFactory;
2524
import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory;
26-
import org.apache.logging.log4j.simple.SimpleLogger;
2725
import org.assertj.core.api.Assertions;
2826
import org.junit.jupiter.api.Test;
2927
import org.mockito.Mockito;
@@ -33,38 +31,19 @@ public class StatusConsoleListenerTest {
3331
public static final MessageFactory MESSAGE_FACTORY = ParameterizedNoReferenceMessageFactory.INSTANCE;
3432

3533
@Test
36-
void SimpleLogger_should_be_used() {
37-
38-
// Create a mock `SimpleLoggerFactory`.
39-
final SimpleLogger logger = Mockito.mock(SimpleLogger.class);
40-
final LogBuilder logBuilder = Mockito.mock(LogBuilder.class);
41-
Mockito.when(logger.atLevel(Mockito.any())).thenReturn(logBuilder);
42-
Mockito.when(logBuilder.withThrowable(Mockito.any())).thenReturn(logBuilder);
43-
Mockito.when(logBuilder.withLocation(Mockito.any())).thenReturn(logBuilder);
44-
final SimpleLoggerFactory loggerFactory = Mockito.mock(SimpleLoggerFactory.class);
45-
Mockito.when(loggerFactory.createSimpleLogger(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
46-
.thenReturn(logger);
34+
void StatusData_getFormattedStatus_should_be_used() {
4735

4836
// Create the listener.
4937
final PrintStream stream = Mockito.mock(PrintStream.class);
50-
final Level level = Mockito.mock(Level.class);
51-
final StatusConsoleListener listener = new StatusConsoleListener(level, stream, loggerFactory);
38+
final StatusConsoleListener listener = new StatusConsoleListener(Level.ALL, stream);
5239

5340
// Log a message.
54-
final StackTraceElement caller = Mockito.mock(StackTraceElement.class);
5541
final Message message = Mockito.mock(Message.class);
56-
final Throwable throwable = Mockito.mock(Throwable.class);
57-
final StatusData statusData = new StatusData(caller, level, message, throwable, null);
42+
final StatusData statusData = Mockito.spy(new StatusData(null, Level.TRACE, message, null, null));
5843
listener.log(statusData);
5944

6045
// Verify the call.
61-
Mockito.verify(loggerFactory)
62-
.createSimpleLogger(
63-
Mockito.eq("StatusConsoleListener"), Mockito.same(level), Mockito.any(), Mockito.same(stream));
64-
Mockito.verify(logger).atLevel(Mockito.same(level));
65-
Mockito.verify(logBuilder).withThrowable(Mockito.same(throwable));
66-
Mockito.verify(logBuilder).withLocation(Mockito.same(caller));
67-
Mockito.verify(logBuilder).log(Mockito.same(message));
46+
Mockito.verify(statusData).getFormattedStatus();
6847
}
6948

7049
@Test

log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.apache.logging.log4j.util.LoaderUtil;
3939
import org.apache.logging.log4j.util.MessageSupplier;
4040
import org.apache.logging.log4j.util.PerformanceSensitive;
41-
import org.apache.logging.log4j.util.PropertiesUtil;
4241
import org.apache.logging.log4j.util.StackLocatorUtil;
4342
import org.apache.logging.log4j.util.Supplier;
4443

@@ -86,14 +85,14 @@ public abstract class AbstractLogger implements ExtendedLogger, LocationAwareLog
8685
/**
8786
* The default MessageFactory class.
8887
*/
89-
public static final Class<? extends MessageFactory> DEFAULT_MESSAGE_FACTORY_CLASS = createClassForProperty(
90-
"log4j2.messageFactory", ReusableMessageFactory.class, ParameterizedMessageFactory.class);
88+
public static final Class<? extends MessageFactory> DEFAULT_MESSAGE_FACTORY_CLASS =
89+
ParameterizedMessageFactory.class;
9190

9291
/**
9392
* The default FlowMessageFactory class.
9493
*/
9594
public static final Class<? extends FlowMessageFactory> DEFAULT_FLOW_MESSAGE_FACTORY_CLASS =
96-
createFlowClassForProperty("log4j2.flowMessageFactory", DefaultFlowMessageFactory.class);
95+
DefaultFlowMessageFactory.class;
9796

9897
private static final long serialVersionUID = 2L;
9998

@@ -198,32 +197,6 @@ protected Message catchingMsg(final Throwable throwable) {
198197
return messageFactory.newMessage(CATCHING);
199198
}
200199

201-
private static Class<? extends MessageFactory> createClassForProperty(
202-
final String property,
203-
final Class<ReusableMessageFactory> reusableParameterizedMessageFactoryClass,
204-
final Class<ParameterizedMessageFactory> parameterizedMessageFactoryClass) {
205-
try {
206-
final String fallback = Constants.ENABLE_THREADLOCALS
207-
? reusableParameterizedMessageFactoryClass.getName()
208-
: parameterizedMessageFactoryClass.getName();
209-
final String clsName = PropertiesUtil.getProperties().getStringProperty(property, fallback);
210-
return LoaderUtil.loadClass(clsName).asSubclass(MessageFactory.class);
211-
} catch (final Throwable throwable) {
212-
return parameterizedMessageFactoryClass;
213-
}
214-
}
215-
216-
private static Class<? extends FlowMessageFactory> createFlowClassForProperty(
217-
final String property, final Class<DefaultFlowMessageFactory> defaultFlowMessageFactoryClass) {
218-
try {
219-
final String clsName = PropertiesUtil.getProperties()
220-
.getStringProperty(property, defaultFlowMessageFactoryClass.getName());
221-
return LoaderUtil.loadClass(clsName).asSubclass(FlowMessageFactory.class);
222-
} catch (final Throwable throwable) {
223-
return defaultFlowMessageFactoryClass;
224-
}
225-
}
226-
227200
private static MessageFactory2 createDefaultMessageFactory() {
228201
try {
229202
final MessageFactory result = LoaderUtil.newInstanceOf(DEFAULT_MESSAGE_FACTORY_CLASS);

log4j-api/src/main/java/org/apache/logging/log4j/status/SimpleLoggerFactory.java

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)