This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/main by this push:
     new 8de3a60bb9 Improve configuration error handling of HttpAppender 
(#3438) (#3865)
8de3a60bb9 is described below

commit 8de3a60bb97dcc15444bbff45d1453807ca68230
Author: Matt Sicker <mattsic...@apache.org>
AuthorDate: Fri Aug 1 12:04:17 2025 -0500

    Improve configuration error handling of HttpAppender (#3438) (#3865)
    
    * Improve configuration error handling of HttpAppender (#3438)
    
    This PR introduces improvements to `HttpAppender` and adds a new test 
class, `HttpAppenderBuilderTest`, to enhance test coverage.
    
    The changes include:
     * Updating `HttpAppender` to improve validating behavior.
     * Adding HttpAppenderBuilderTest.java to verify the builder logic for 
HttpAppender.
    
    Ensuring that missing configurations (e.g., URL, Layout) correctly log 
errors.
    
    Co-authored-by: Piotr P. Karwasz <piotr.git...@karwasz.org>
    
    * Fix compilation errors in HttpAppenderBuilderTest
    
    ---------
    
    Co-authored-by: Suvrat Acharya 
<140749446+suvrat1...@users.noreply.github.com>
    Co-authored-by: Piotr P. Karwasz <piotr.git...@karwasz.org>
---
 .../core/appender/HttpAppenderBuilderTest.java     | 130 +++++++++++++++++++++
 .../logging/log4j/core/appender/HttpAppender.java  |  24 ++--
 2 files changed, 147 insertions(+), 7 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
new file mode 100644
index 0000000000..e61e9abcc6
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+class HttpAppenderBuilderTest {
+
+    private HttpAppender.Builder<?> getBuilder() {
+        Configuration mockConfig = new DefaultConfiguration();
+        return 
HttpAppender.newBuilder().setConfiguration(mockConfig).setName("TestHttpAppender");
 // Name is required
+    }
+
+    @Test
+    @UsingStatusListener
+    void testBuilderWithoutUrl(final ListStatusListener listener) throws 
Exception {
+        HttpAppender appender = HttpAppender.newBuilder()
+                .setConfiguration(new DefaultConfiguration())
+                .setName("TestAppender")
+                .setLayout(PatternLayout.createDefaultLayout()) // Providing a 
layout here
+                .build();
+
+        assertThat(listener.findStatusData(Level.ERROR))
+                .anyMatch(statusData ->
+                        
statusData.getMessage().getFormattedMessage().contains("HttpAppender requires 
URL to be set."));
+    }
+
+    @Test
+    @UsingStatusListener
+    void testBuilderWithUrlAndWithoutLayout(final ListStatusListener listener) 
throws Exception {
+        HttpAppender appender = HttpAppender.newBuilder()
+                .setConfiguration(new DefaultConfiguration())
+                .setName("TestAppender")
+                .setUrl(new URL("http://localhost:8080/logs";))
+                .build();
+
+        assertThat(listener.findStatusData(Level.ERROR)).anyMatch(statusData 
-> statusData
+                .getMessage()
+                .getFormattedMessage()
+                .contains("HttpAppender requires a layout to be set."));
+    }
+
+    @Test
+    void testBuilderWithValidConfiguration() throws Exception {
+        URL url = new URL("http://example.com";);
+        Layout layout = PatternLayout.createDefaultLayout();
+
+        HttpAppender.Builder<?> builder = 
getBuilder().setUrl(url).setLayout(layout);
+
+        HttpAppender appender = builder.build();
+        assertNotNull(appender, "HttpAppender should be created with valid 
configuration.");
+    }
+
+    @Test
+    void testBuilderWithCustomMethod() throws Exception {
+        URL url = new URL("http://example.com";);
+        Layout layout = PatternLayout.createDefaultLayout();
+        String customMethod = "PUT";
+
+        HttpAppender.Builder<?> builder =
+                
getBuilder().setUrl(url).setLayout(layout).setMethod(customMethod);
+
+        HttpAppender appender = builder.build();
+        assertNotNull(appender, "HttpAppender should be created with a custom 
HTTP method.");
+    }
+
+    @Test
+    void testBuilderWithHeaders() throws Exception {
+        URL url = new URL("http://example.com";);
+        Layout layout = PatternLayout.createDefaultLayout();
+        Property[] headers = new Property[] {
+            Property.createProperty("Header1", "Value1"), 
Property.createProperty("Header2", "Value2")
+        };
+
+        HttpAppender.Builder<?> builder =
+                getBuilder().setUrl(url).setLayout(layout).setHeaders(headers);
+
+        HttpAppender appender = builder.build();
+        assertNotNull(appender, "HttpAppender should be created with 
headers.");
+    }
+
+    @Test
+    void testBuilderWithSslConfiguration() throws Exception {
+        URL url = new URL("https://example.com";);
+        Layout layout = PatternLayout.createDefaultLayout();
+
+        // Use real SslConfiguration instead of Mockito mock
+        SslConfiguration sslConfig = 
SslConfiguration.createSSLConfiguration(null, null, null, false);
+
+        HttpAppender.Builder<?> builder =
+                
getBuilder().setUrl(url).setLayout(layout).setSslConfiguration(sslConfig);
+
+        HttpAppender appender = builder.build();
+        assertNotNull(appender, "HttpAppender should be created with SSL 
configuration.");
+    }
+
+    @Test
+    void testBuilderWithInvalidUrl() {
+        assertThrows(MalformedURLException.class, () -> new 
URL("invalid-url"));
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
index e33de5cc66..b34b546f9d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
@@ -31,17 +31,16 @@ import 
org.apache.logging.log4j.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.plugins.PluginElement;
 import org.apache.logging.log4j.plugins.PluginFactory;
 import org.apache.logging.log4j.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.status.StatusLogger;
 
-/**
- * Sends log events over HTTP.
- */
 @Configurable(elementType = Appender.ELEMENT_TYPE, printObject = true)
 @Plugin("Http")
 public final class HttpAppender extends AbstractAppender {
 
+    private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
     /**
      * Builds HttpAppender instances.
-     * @param <B> The type to build
      */
     public static class Builder<B extends Builder<B>> extends 
AbstractAppender.Builder<B>
             implements 
org.apache.logging.log4j.plugins.util.Builder<HttpAppender> {
@@ -70,6 +69,18 @@ public final class HttpAppender extends AbstractAppender {
 
         @Override
         public HttpAppender build() {
+            // Validate URL presence
+            if (url == null) {
+                LOGGER.error("HttpAppender requires URL to be set.");
+                return null; // Return null if URL is missing
+            }
+
+            // Validate layout presence
+            if (getLayout() == null) {
+                LOGGER.error("HttpAppender requires a layout to be set.");
+                return null; // Return null if layout is missing
+            }
+
             final HttpManager httpManager = new HttpURLConnectionManager(
                     getConfiguration(),
                     getConfiguration().getLoggerContext(),
@@ -81,10 +92,12 @@ public final class HttpAppender extends AbstractAppender {
                     headers,
                     sslConfiguration,
                     verifyHostname);
+
             return new HttpAppender(
                     getName(), getLayout(), getFilter(), isIgnoreExceptions(), 
httpManager, getPropertyArray());
         }
 
+        // Getter and Setter methods
         public URL getUrl() {
             return url;
         }
@@ -149,9 +162,6 @@ public final class HttpAppender extends AbstractAppender {
         }
     }
 
-    /**
-     * @return a builder for a HttpAppender.
-     */
     @PluginFactory
     public static <B extends Builder<B>> B newBuilder() {
         return new Builder<B>().asBuilder();

Reply via email to