This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit b08d46636c0029ddf83ee5b843901621d3dec8c0 Author: Gary Gregory <[email protected]> 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 2dc1337..c7e29b0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -322,6 +322,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">
