You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/02/05 14:09:46 UTC

[logging-log4j2] branch release-2.x updated: Log4j 1.2 bridge now supports SocketAppender.

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

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 6573f8a  Log4j 1.2 bridge now supports SocketAppender.
6573f8a is described below

commit 6573f8a4ed58112c15748aa4a34a051754b68be9
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 09:09:42 2022 -0500

    Log4j 1.2 bridge now supports SocketAppender.
    
    First commit.
    TODO: multicast support.
---
 .../builders/appender/SocketAppenderBuilder.java   | 164 +++++++++++++++++++++
 .../config/SocketAppenderConfigurationTest.java    |  80 ++++++++++
 .../src/test/resources/log4j1-socket.properties    |  25 ++++
 log4j-1.2-api/src/test/resources/log4j1-socket.xml |  36 +++++
 src/changes/changes.xml                            |   3 +
 5 files changed, 308 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
new file mode 100644
index 0000000..e16e140
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -0,0 +1,164 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SocketAppender", category = CATEGORY)
+public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    private static final String HOST_PARAM = "RemoteHost";
+    private static final String PORT_PARAM = "Port";
+    private static final String RECONNECTION_DELAY_PARAM = "ReconnectionDelay";
+    private static final int DEFAULT_PORT = 4560;
+
+    /**
+     * The default reconnection delay (30000 milliseconds or 30 seconds).
+     */
+    private static final int DEFAULT_RECONNECTION_DELAY = 30_000;
+
+    public static final Logger LOGGER = StatusLogger.getLogger();
+
+    public SocketAppenderBuilder() {
+    }
+
+    public SocketAppenderBuilder(final String prefix, final Properties props) {
+        super(prefix, props);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(final String name, final String host, final int port, final Layout layout,
+        final Filter filter, final String level, final boolean immediateFlush, final int reconnectDelayMillis, final T configuration) {
+        org.apache.logging.log4j.core.Layout<?> actualLayout = null;
+        if (layout instanceof LayoutWrapper) {
+            actualLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            actualLayout = new LayoutAdapter(layout);
+        }
+        final org.apache.logging.log4j.core.Filter actualFilter = buildFilters(level, filter);
+        // @formatter:off
+        return new AppenderWrapper(SocketAppender.newBuilder()
+            .withHost(host)
+            .withPort(port)
+            .withReconnectDelayMillis(reconnectDelayMillis)
+            .setName(name)
+            .setLayout(actualLayout)
+            .setFilter(actualFilter)
+            .setConfiguration(configuration)
+            .setImmediateFlush(immediateFlush)
+            .build());
+        // @formatter:on
+    }
+
+    @Override
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+        final String name = getNameAttribute(appenderElement);
+        final AtomicReference<String> host = new AtomicReference<>("localhost");
+        final AtomicInteger port = new AtomicInteger(DEFAULT_PORT);
+        final AtomicInteger reconnectDelay = new AtomicInteger(DEFAULT_RECONNECTION_DELAY);
+        final AtomicReference<Layout> layout = new AtomicReference<>();
+        final AtomicReference<List<Filter>> filters = new AtomicReference<>(new ArrayList<>());
+        final AtomicReference<String> level = new AtomicReference<>();
+        final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+        forEachElement(appenderElement.getChildNodes(), currentElement -> {
+            switch (currentElement.getTagName()) {
+            case LAYOUT_TAG:
+                layout.set(config.parseLayout(currentElement));
+                break;
+            case FILTER_TAG:
+                filters.get().add(config.parseFilters(currentElement));
+                break;
+            case PARAM_TAG:
+                switch (getNameAttributeKey(currentElement)) {
+                case HOST_PARAM:
+                    set(HOST_PARAM, currentElement, host);
+                    break;
+                case PORT_PARAM:
+                    set(PORT_PARAM, currentElement, port);
+                    break;
+                case RECONNECTION_DELAY_PARAM:
+                    set(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
+                    break;
+                case THRESHOLD_PARAM:
+                    set(THRESHOLD_PARAM, currentElement, level);
+                    break;
+                case IMMEDIATE_FLUSH_PARAM:
+                    set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+                    break;
+                }
+                break;
+            }
+        });
+        Filter head = null;
+        Filter current = null;
+        for (final Filter f : filters.get()) {
+            if (head == null) {
+                head = f;
+            } else {
+                current.next = f;
+            }
+            current = f;
+        }
+        return createAppender(name, host.get(), port.get(), layout.get(), head, level.get(), immediateFlush.get(), reconnectDelay.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix, final String filterPrefix, final Properties props,
+        final PropertiesConfiguration configuration) {
+        // @formatter:off
+        return createAppender(name,
+            getProperty(HOST_PARAM),
+            getIntegerProperty(PORT_PARAM, DEFAULT_PORT),
+            configuration.parseLayout(layoutPrefix, name, props),
+            configuration.parseAppenderFilters(props, filterPrefix, name),
+            getProperty(THRESHOLD_PARAM),
+            getBooleanProperty(IMMEDIATE_FLUSH_PARAM),
+            getIntegerProperty(RECONNECTION_DELAY_PARAM, DEFAULT_RECONNECTION_DELAY),
+            configuration);
+        // @formatter:on
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
new file mode 100644
index 0000000..5fbaf30
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.core.net.TcpSocketManager;
+import org.junit.Test;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+public class SocketAppenderConfigurationTest {
+
+    private void check(final Protocol expected, final Configuration configuration) {
+        final Map<String, Appender> appenders = configuration.getAppenders();
+        assertNotNull(appenders);
+        final String appenderName = "socket";
+        final Appender appender = appenders.get(appenderName);
+        assertNotNull(appender, "Missing appender " + appenderName);
+        final SocketAppender syslogAppender = (SocketAppender) appender;
+        @SuppressWarnings("resource")
+        final TcpSocketManager manager = (TcpSocketManager) syslogAppender.getManager();
+        final String prefix = expected + ":";
+        assertTrue(manager.getName().startsWith(prefix), () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+        // Threshold
+        final ThresholdFilter filter = (ThresholdFilter) syslogAppender.getFilter();
+        assertEquals(Level.DEBUG, filter.getLevel());
+        // Host
+        assertEquals("localhost", manager.getHost());
+        // Port
+        assertEquals(9999, manager.getPort());
+        // Port
+        assertEquals(100, manager.getReconnectionDelayMillis());
+    }
+
+    private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+        check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+    }
+
+    private void checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+        check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+    }
+
+    @Test
+    public void testProperties() throws Exception {
+        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.properties");
+    }
+
+    @Test
+    public void testXml() throws Exception {
+        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.xml");
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.properties b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
new file mode 100644
index 0000000..3072b46
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.PatternLayout
+log4j.appender.socket.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.xml b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
new file mode 100644
index 0000000..eca3a0f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="socket" class="org.apache.log4j.net.SocketAppender">
+    <param name="RemoteHost" value="localhost"/>
+    <param name="Port" value="9999"/>
+    <param name="ReconnectionDelay" value="100"/>
+    <param name="Threshold" value="DEBUG"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+    </layout>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="syslog" />
+  </root>
+
+</log4j:configuration>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d8cb19f..75eb9bb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -160,6 +160,9 @@
       <action dev="ggregory" type="fix" due-to="Piotr P. Karwasz">
         Log4j 1.2 bridge uses some incorrect default property values in some appenders.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge supports the SocketAppender.
+      </action>
       <action dev="ggregory" type="fix" due-to="Gary Gregory, Piotr P. Karwasz">
       </action>
       <action dev="ggregory" type="fix">