Skip to content

Commit bfceeb4

Browse files
committed
HttpAppender fix and HttpAppenderBuilderTest
1 parent 94d3227 commit bfceeb4

File tree

2 files changed

+165
-8
lines changed

2 files changed

+165
-8
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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 org.apache.logging.log4j.Level;
20+
import org.apache.logging.log4j.core.Layout;
21+
import org.apache.logging.log4j.core.config.Configuration;
22+
import org.apache.logging.log4j.core.config.DefaultConfiguration;
23+
import org.apache.logging.log4j.core.config.Property;
24+
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
25+
import org.apache.logging.log4j.core.layout.JsonLayout;
26+
import org.apache.logging.log4j.status.StatusLogger;
27+
import org.apache.logging.log4j.test.ListStatusListener;
28+
import org.apache.logging.log4j.test.junit.UsingStatusListener;
29+
import org.junit.jupiter.api.Test;
30+
import org.mockito.Mockito;
31+
32+
import java.net.MalformedURLException;
33+
import java.net.URL;
34+
35+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
36+
import static org.junit.jupiter.api.Assertions.*;
37+
import static org.mockito.Mockito.*;
38+
39+
class HttpAppenderBuilderTest {
40+
41+
// Mock the LOGGER to verify error logs
42+
private final StatusLogger mockStatusLogger = Mockito.mock(StatusLogger.class);
43+
44+
private HttpAppender.Builder<?> getBuilder() {
45+
Configuration mockConfig = new DefaultConfiguration();
46+
return HttpAppender.newBuilder()
47+
.setConfiguration(mockConfig)
48+
.setName("TestHttpAppender"); // Name is required
49+
}
50+
51+
@Test
52+
@UsingStatusListener
53+
void testBuilderWithoutUrl(final ListStatusListener listener) throws Exception {
54+
// Build the HttpAppender without URL
55+
HttpAppender appender = HttpAppender.newBuilder()
56+
.setConfiguration(new DefaultConfiguration())
57+
.setName("TestAppender")
58+
.setLayout(JsonLayout.createDefaultLayout()) // Providing a layout here
59+
.build();
60+
61+
// Verify that the error message for missing URL is captured
62+
assertThat(listener.findStatusData(Level.ERROR))
63+
.anyMatch(statusData -> statusData.getMessage().getFormattedMessage().contains("HttpAppender requires URL to be set."));
64+
}
65+
66+
@Test
67+
@UsingStatusListener
68+
void testBuilderWithUrlAndWithoutLayout(final ListStatusListener listener) throws Exception {
69+
// Build the HttpAppender with URL but without Layout
70+
HttpAppender appender = HttpAppender.newBuilder()
71+
.setConfiguration(new DefaultConfiguration())
72+
.setName("TestAppender")
73+
.setUrl(new URL("http://localhost:8080/logs")) // Providing the URL
74+
.build();
75+
76+
// Verify that the error message for missing layout is captured
77+
assertThat(listener.findStatusData(Level.ERROR))
78+
.anyMatch(statusData -> statusData.getMessage().getFormattedMessage().contains("HttpAppender requires a layout to be set."));
79+
}
80+
81+
82+
@Test
83+
void testBuilderWithValidConfiguration() throws Exception {
84+
URL url = new URL("http://example.com");
85+
Layout<?> layout = JsonLayout.createDefaultLayout(); // Valid layout
86+
87+
HttpAppender.Builder<?> builder = getBuilder()
88+
.setUrl(url)
89+
.setLayout(layout);
90+
91+
HttpAppender appender = builder.build();
92+
assertNotNull(appender, "HttpAppender should be created with valid configuration.");
93+
}
94+
95+
@Test
96+
void testBuilderWithCustomMethod() throws Exception {
97+
URL url = new URL("http://example.com");
98+
Layout<?> layout = JsonLayout.createDefaultLayout();
99+
String customMethod = "PUT";
100+
101+
HttpAppender.Builder<?> builder = getBuilder()
102+
.setUrl(url)
103+
.setLayout(layout)
104+
.setMethod(customMethod);
105+
106+
HttpAppender appender = builder.build();
107+
assertNotNull(appender, "HttpAppender should be created with a custom HTTP method.");
108+
}
109+
110+
@Test
111+
void testBuilderWithHeaders() throws Exception {
112+
URL url = new URL("http://example.com");
113+
Layout<?> layout = JsonLayout.createDefaultLayout();
114+
Property[] headers = new Property[]{
115+
Property.createProperty("Header1", "Value1"),
116+
Property.createProperty("Header2", "Value2")
117+
};
118+
119+
HttpAppender.Builder<?> builder = getBuilder()
120+
.setUrl(url)
121+
.setLayout(layout)
122+
.setHeaders(headers);
123+
124+
HttpAppender appender = builder.build();
125+
assertNotNull(appender, "HttpAppender should be created with headers.");
126+
}
127+
128+
@Test
129+
void testBuilderWithSslConfiguration() throws Exception {
130+
URL url = new URL("https://example.com");
131+
Layout<?> layout = JsonLayout.createDefaultLayout();
132+
SslConfiguration sslConfig = mock(SslConfiguration.class);
133+
134+
HttpAppender.Builder<?> builder = getBuilder()
135+
.setUrl(url)
136+
.setLayout(layout)
137+
.setSslConfiguration(sslConfig);
138+
139+
HttpAppender appender = builder.build();
140+
assertNotNull(appender, "HttpAppender should be created with SSL configuration.");
141+
}
142+
143+
@Test
144+
void testBuilderWithInvalidUrl() {
145+
assertThrows(MalformedURLException.class, () -> new URL("invalid-url"));
146+
}
147+
}

log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,15 @@
3232
import org.apache.logging.log4j.core.config.plugins.PluginElement;
3333
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
3434
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
35+
import org.apache.logging.log4j.status.StatusLogger;
3536

36-
/**
37-
* Sends log events over HTTP.
38-
*/
3937
@Plugin(name = "Http", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
4038
public final class HttpAppender extends AbstractAppender {
4139

40+
private static final StatusLogger LOGGER = StatusLogger.getLogger();
41+
4242
/**
4343
* Builds HttpAppender instances.
44-
* @param <B> The type to build
4544
*/
4645
public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
4746
implements org.apache.logging.log4j.core.util.Builder<HttpAppender> {
@@ -70,6 +69,18 @@ public static class Builder<B extends Builder<B>> extends AbstractAppender.Build
7069

7170
@Override
7271
public HttpAppender build() {
72+
// Validate URL presence
73+
if (url == null) {
74+
LOGGER.error("HttpAppender requires URL to be set.");
75+
return null; // Return null if URL is missing
76+
}
77+
78+
// Validate layout presence
79+
if (getLayout() == null) {
80+
LOGGER.error("HttpAppender requires a layout to be set.");
81+
return null; // Return null if layout is missing
82+
}
83+
7384
final HttpManager httpManager = new HttpURLConnectionManager(
7485
getConfiguration(),
7586
getConfiguration().getLoggerContext(),
@@ -81,10 +92,12 @@ public HttpAppender build() {
8192
headers,
8293
sslConfiguration,
8394
verifyHostname);
95+
8496
return new HttpAppender(
8597
getName(), getLayout(), getFilter(), isIgnoreExceptions(), httpManager, getPropertyArray());
8698
}
8799

100+
// Getter and Setter methods
88101
public URL getUrl() {
89102
return url;
90103
}
@@ -149,9 +162,6 @@ public B setVerifyHostname(final boolean verifyHostname) {
149162
}
150163
}
151164

152-
/**
153-
* @return a builder for a HttpAppender.
154-
*/
155165
@PluginBuilderFactory
156166
public static <B extends Builder<B>> B newBuilder() {
157167
return new Builder<B>().asBuilder();
@@ -199,4 +209,4 @@ public boolean stop(final long timeout, final TimeUnit timeUnit) {
199209
public String toString() {
200210
return "HttpAppender{" + "name=" + getName() + ", state=" + getState() + '}';
201211
}
202-
}
212+
}

0 commit comments

Comments
 (0)