Skip to content

Commit fc17a6f

Browse files
authored
Merge branch 'main' into feature/3.x/merge-log-event-improvements
2 parents bff263e + e3ced8b commit fc17a6f

File tree

13 files changed

+360
-134
lines changed

13 files changed

+360
-134
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/AsyncAppenderTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,11 @@ public void testShutdownTimeout(final LoggerContext context) {
151151
context.getLogger("Logger").info("This is a test");
152152
context.stop();
153153
}
154+
155+
@Test
156+
@LoggerContextSource("log4j-asynch-location.xml")
157+
public void testRequiresLocation(final LoggerContext context) {
158+
final AsyncAppender appender = context.getConfiguration().getAppender("Async");
159+
assertTrue(appender.requiresLocation());
160+
}
154161
}

log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FailoverAppenderTest.java

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
2020
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
2122

2223
import java.util.List;
2324
import org.apache.logging.log4j.Logger;
@@ -27,61 +28,52 @@
2728
import org.apache.logging.log4j.core.test.appender.ListAppender;
2829
import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
2930
import org.apache.logging.log4j.core.test.junit.Named;
30-
import org.junit.jupiter.api.AfterEach;
31-
import org.junit.jupiter.api.Tag;
3231
import org.junit.jupiter.api.Test;
3332

34-
@LoggerContextSource("log4j-failover.xml")
35-
@Tag("sleepy")
3633
public class FailoverAppenderTest {
37-
private final ListAppender app;
38-
private final FailOnceAppender foApp;
39-
private final Logger logger;
40-
private final Logger onceLogger;
41-
42-
public FailoverAppenderTest(
43-
final LoggerContext context,
44-
@Named("List") final ListAppender app,
45-
@Named("Once") final FailOnceAppender foApp) {
46-
this.app = app;
47-
this.foApp = foApp;
48-
logger = context.getLogger("LoggerTest");
49-
onceLogger = context.getLogger("Once");
50-
}
51-
52-
@AfterEach
53-
public void tearDown() throws Exception {
54-
app.clear();
55-
}
5634

5735
@Test
58-
public void testFailover() {
36+
@LoggerContextSource("log4j-failover.xml")
37+
void testFailover(final LoggerContext context, @Named("List") final ListAppender app) {
38+
final Logger logger = context.getLogger("LoggerTest");
5939
logger.error("This is a test");
6040
List<LogEvent> events = app.getEvents();
6141
assertNotNull(events);
62-
assertEquals(events.size(), 1, "Incorrect number of events. Should be 1 is " + events.size());
42+
assertEquals(1, events.size(), "Incorrect number of events. Should be 1 is " + events.size());
6343
app.clear();
6444
logger.error("This is a test");
6545
events = app.getEvents();
6646
assertNotNull(events);
67-
assertEquals(events.size(), 1, "Incorrect number of events. Should be 1 is " + events.size());
47+
assertEquals(1, events.size(), "Incorrect number of events. Should be 1 is " + events.size());
6848
}
6949

7050
@Test
71-
@Tag("sleepy")
72-
public void testRecovery() throws Exception {
51+
@LoggerContextSource("log4j-failover.xml")
52+
void testRecovery(
53+
final LoggerContext context,
54+
@Named("List") final ListAppender app,
55+
@Named("Once") final FailOnceAppender foApp)
56+
throws Exception {
57+
final Logger onceLogger = context.getLogger("Once");
7358
onceLogger.error("Fail once");
7459
onceLogger.error("Fail again");
7560
List<LogEvent> events = app.getEvents();
7661
assertNotNull(events);
77-
assertEquals(events.size(), 2, "Incorrect number of events. Should be 2 is " + events.size());
62+
assertEquals(2, events.size(), "Incorrect number of events. Should be 2 is " + events.size());
7863
app.clear();
7964
Thread.sleep(1100);
8065
onceLogger.error("Fail after recovery interval");
8166
onceLogger.error("Second log message");
8267
events = app.getEvents();
83-
assertEquals(events.size(), 0, "Did not recover");
68+
assertEquals(0, events.size(), "Did not recover");
8469
events = foApp.drainEvents();
85-
assertEquals(events.size(), 2, "Incorrect number of events in primary appender");
70+
assertEquals(2, events.size(), "Incorrect number of events in primary appender");
71+
}
72+
73+
@Test
74+
@LoggerContextSource("log4j-failover-location.xml")
75+
void testRequiresLocation(final LoggerContext context) {
76+
final FailoverAppender appender = context.getConfiguration().getAppender("Failover");
77+
assertTrue(appender.requiresLocation());
8678
}
8779
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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.core.appender;
18+
19+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
22+
23+
import java.net.MalformedURLException;
24+
import java.net.URL;
25+
import org.apache.logging.log4j.Level;
26+
import org.apache.logging.log4j.core.Layout;
27+
import org.apache.logging.log4j.core.config.Configuration;
28+
import org.apache.logging.log4j.core.config.DefaultConfiguration;
29+
import org.apache.logging.log4j.core.config.Property;
30+
import org.apache.logging.log4j.core.layout.PatternLayout;
31+
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
32+
import org.apache.logging.log4j.test.ListStatusListener;
33+
import org.apache.logging.log4j.test.junit.UsingStatusListener;
34+
import org.junit.jupiter.api.Test;
35+
36+
class HttpAppenderBuilderTest {
37+
38+
private HttpAppender.Builder<?> getBuilder() {
39+
Configuration mockConfig = new DefaultConfiguration();
40+
return HttpAppender.newBuilder().setConfiguration(mockConfig).setName("TestHttpAppender"); // Name is required
41+
}
42+
43+
@Test
44+
@UsingStatusListener
45+
void testBuilderWithoutUrl(final ListStatusListener listener) throws Exception {
46+
HttpAppender appender = HttpAppender.newBuilder()
47+
.setConfiguration(new DefaultConfiguration())
48+
.setName("TestAppender")
49+
.setLayout(PatternLayout.createDefaultLayout()) // Providing a layout here
50+
.build();
51+
52+
assertThat(listener.findStatusData(Level.ERROR))
53+
.anyMatch(statusData ->
54+
statusData.getMessage().getFormattedMessage().contains("HttpAppender requires URL to be set."));
55+
}
56+
57+
@Test
58+
@UsingStatusListener
59+
void testBuilderWithUrlAndWithoutLayout(final ListStatusListener listener) throws Exception {
60+
HttpAppender appender = HttpAppender.newBuilder()
61+
.setConfiguration(new DefaultConfiguration())
62+
.setName("TestAppender")
63+
.setUrl(new URL("http://localhost:8080/logs"))
64+
.build();
65+
66+
assertThat(listener.findStatusData(Level.ERROR)).anyMatch(statusData -> statusData
67+
.getMessage()
68+
.getFormattedMessage()
69+
.contains("HttpAppender requires a layout to be set."));
70+
}
71+
72+
@Test
73+
void testBuilderWithValidConfiguration() throws Exception {
74+
URL url = new URL("http://example.com");
75+
Layout layout = PatternLayout.createDefaultLayout();
76+
77+
HttpAppender.Builder<?> builder = getBuilder().setUrl(url).setLayout(layout);
78+
79+
HttpAppender appender = builder.build();
80+
assertNotNull(appender, "HttpAppender should be created with valid configuration.");
81+
}
82+
83+
@Test
84+
void testBuilderWithCustomMethod() throws Exception {
85+
URL url = new URL("http://example.com");
86+
Layout layout = PatternLayout.createDefaultLayout();
87+
String customMethod = "PUT";
88+
89+
HttpAppender.Builder<?> builder =
90+
getBuilder().setUrl(url).setLayout(layout).setMethod(customMethod);
91+
92+
HttpAppender appender = builder.build();
93+
assertNotNull(appender, "HttpAppender should be created with a custom HTTP method.");
94+
}
95+
96+
@Test
97+
void testBuilderWithHeaders() throws Exception {
98+
URL url = new URL("http://example.com");
99+
Layout layout = PatternLayout.createDefaultLayout();
100+
Property[] headers = new Property[] {
101+
Property.createProperty("Header1", "Value1"), Property.createProperty("Header2", "Value2")
102+
};
103+
104+
HttpAppender.Builder<?> builder =
105+
getBuilder().setUrl(url).setLayout(layout).setHeaders(headers);
106+
107+
HttpAppender appender = builder.build();
108+
assertNotNull(appender, "HttpAppender should be created with headers.");
109+
}
110+
111+
@Test
112+
void testBuilderWithSslConfiguration() throws Exception {
113+
URL url = new URL("https://example.com");
114+
Layout layout = PatternLayout.createDefaultLayout();
115+
116+
// Use real SslConfiguration instead of Mockito mock
117+
SslConfiguration sslConfig = SslConfiguration.createSSLConfiguration(null, null, null, false);
118+
119+
HttpAppender.Builder<?> builder =
120+
getBuilder().setUrl(url).setLayout(layout).setSslConfiguration(sslConfig);
121+
122+
HttpAppender appender = builder.build();
123+
assertNotNull(appender, "HttpAppender should be created with SSL configuration.");
124+
}
125+
126+
@Test
127+
void testBuilderWithInvalidUrl() {
128+
assertThrows(MalformedURLException.class, () -> new URL("invalid-url"));
129+
}
130+
}

log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ static List<Arguments> sequencingTestCases() {
6565
testCases.add(Arguments.of(
6666
"yyyyMMddHHmmssSSSX",
6767
ChronoUnit.HOURS,
68-
asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec("", 3), pDyn("X"))));
68+
asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec(2, "", 3), pDyn("X"))));
6969

7070
// `yyyyMMddHHmmssSSSX` instant cache updated per minute
7171
testCases.add(Arguments.of(
7272
"yyyyMMddHHmmssSSSX",
7373
ChronoUnit.MINUTES,
74-
asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec("", 3), pDyn("X"))));
74+
asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec(2, "", 3), pDyn("X"))));
7575

7676
// ISO9601 instant cache updated daily
7777
final String iso8601InstantPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
@@ -81,24 +81,29 @@ static List<Arguments> sequencingTestCases() {
8181
asList(
8282
pDyn("yyyy'-'MM'-'dd'T'", ChronoUnit.DAYS),
8383
pDyn("HH':'mm':'", ChronoUnit.MINUTES),
84-
pSec(".", 3),
84+
pSec(2, ".", 3),
8585
pDyn("X"))));
8686

8787
// ISO9601 instant cache updated per minute
8888
testCases.add(Arguments.of(
8989
iso8601InstantPattern,
9090
ChronoUnit.MINUTES,
91-
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
91+
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
9292

9393
// ISO9601 instant cache updated per second
9494
testCases.add(Arguments.of(
9595
iso8601InstantPattern,
9696
ChronoUnit.SECONDS,
97-
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
97+
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
9898

9999
// Seconds and micros
100100
testCases.add(Arguments.of(
101-
"HH:mm:ss.SSSSSS", ChronoUnit.MINUTES, asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 6))));
101+
"HH:mm:ss.SSSSSS",
102+
ChronoUnit.MINUTES,
103+
asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 6))));
104+
105+
// Seconds without padding
106+
testCases.add(Arguments.of("s.SSS", ChronoUnit.SECONDS, singletonList(pSec(1, ".", 3))));
102107

103108
return testCases;
104109
}
@@ -111,12 +116,12 @@ private static DynamicPatternSequence pDyn(final String pattern, final ChronoUni
111116
return new DynamicPatternSequence(pattern, precision);
112117
}
113118

114-
private static SecondPatternSequence pSec(String separator, int fractionalDigits) {
115-
return new SecondPatternSequence(true, separator, fractionalDigits);
119+
private static SecondPatternSequence pSec(int secondDigits, String separator, int fractionalDigits) {
120+
return new SecondPatternSequence(secondDigits, separator, fractionalDigits);
116121
}
117122

118123
private static SecondPatternSequence pMilliSec() {
119-
return new SecondPatternSequence(false, "", 3);
124+
return new SecondPatternSequence(0, "", 3);
120125
}
121126

122127
@ParameterizedTest
@@ -352,7 +357,9 @@ static Stream<Arguments> formatterInputs() {
352357
"yyyy-MM-dd'T'HH:mm:ss.SSS",
353358
"yyyy-MM-dd'T'HH:mm:ss.SSSSSS",
354359
"dd/MM/yy HH:mm:ss.SSS",
355-
"dd/MM/yyyy HH:mm:ss.SSS")
360+
"dd/MM/yyyy HH:mm:ss.SSS",
361+
// seconds without padding
362+
"s.SSS")
356363
.flatMap(InstantPatternDynamicFormatterTest::formatterInputs);
357364
}
358365

@@ -364,7 +371,7 @@ static Stream<Arguments> formatterInputs() {
364371
Arrays.stream(TimeZone.getAvailableIDs()).map(TimeZone::getTimeZone).toArray(TimeZone[]::new);
365372

366373
static Stream<Arguments> formatterInputs(final String pattern) {
367-
return IntStream.range(0, 500).mapToObj(ignoredIndex -> {
374+
return IntStream.range(0, 100).mapToObj(ignoredIndex -> {
368375
final Locale locale = LOCALES[RANDOM.nextInt(LOCALES.length)];
369376
final TimeZone timeZone = TIME_ZONES[RANDOM.nextInt(TIME_ZONES.length)];
370377
final MutableInstant instant = randomInstant();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to you under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing, software
13+
~ distributed under the License is distributed on an "AS IS" BASIS,
14+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
~ See the License for the specific language governing permissions and
16+
~ limitations under the License.
17+
-->
18+
<Configuration status="OFF">
19+
20+
<Appenders>
21+
<Console name="STDOUT">
22+
<PatternLayout pattern="%m%L%n"/>
23+
</Console>
24+
<Async name="Async" includeLocation="true">
25+
<AppenderRef ref="STDOUT"/>
26+
</Async>
27+
</Appenders>
28+
29+
<Loggers>
30+
<Root level="debug" includeLocation="true">
31+
<AppenderRef ref="Async"/>
32+
</Root>
33+
</Loggers>
34+
35+
</Configuration>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to you under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing, software
13+
~ distributed under the License is distributed on an "AS IS" BASIS,
14+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
~ See the License for the specific language governing permissions and
16+
~ limitations under the License.
17+
-->
18+
<Configuration status="OFF" name="RoutingTest">
19+
20+
<Appenders>
21+
<Console name="CONSOLE">
22+
<PatternLayout pattern="%m%L%n"/>
23+
</Console>
24+
<Console name="CONSOLE2">
25+
<PatternLayout pattern="%m%L%n"/>
26+
</Console>
27+
<Failover name="Failover" primary="CONSOLE">
28+
<Failovers>
29+
<AppenderRef ref="CONSOLE2"/>
30+
</Failovers>
31+
</Failover>
32+
</Appenders>
33+
34+
<Loggers>
35+
<Root level="debug" includeLocation="true">
36+
<AppenderRef ref="Failover"/>
37+
</Root>
38+
</Loggers>
39+
40+
</Configuration>

0 commit comments

Comments
 (0)