Repository: logging-log4j2 Updated Branches: refs/heads/master 06fe5e6d1 -> 92f1c6528
[LOG4J2-1707] and [LOG4J2-1708]. [LOG4J2-1707] Allow TCP Socket Appender to set socket options. [LOG4J2-1708]Allow Secure Socket Appender to set socket options. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/92f1c652 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/92f1c652 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/92f1c652 Branch: refs/heads/master Commit: 92f1c65282cb37d95c7398b562ed2a80e6cfa02a Parents: 06fe5e6 Author: Gary Gregory <[email protected]> Authored: Mon Nov 14 00:24:42 2016 -0800 Committer: Gary Gregory <[email protected]> Committed: Mon Nov 14 00:24:42 2016 -0800 ---------------------------------------------------------------------- .../log4j/core/appender/SocketAppender.java | 33 ++- .../log4j/core/appender/SyslogAppender.java | 37 ++++ .../log4j/core/net/Rfc1349TrafficClass.java | 50 +++++ .../logging/log4j/core/net/SocketOptions.java | 222 +++++++++++++++++++ .../core/net/SocketPerformancePreferences.java | 82 +++++++ .../log4j/core/net/SslSocketManager.java | 88 ++++++-- .../log4j/core/net/TcpSocketManager.java | 190 ++++++++++++---- .../SecureSocketAppenderSocketOptionsTest.java | 119 ++++++++++ .../SocketAppenderSocketOptionsTest.java | 99 +++++++++ .../log4j/core/appender/SocketAppenderTest.java | 7 +- .../src/test/resources/log4j-socket-options.xml | 32 +++ .../test/resources/log4j-ssl-socket-options.xml | 39 ++++ src/changes/changes.xml | 6 + 13 files changed, 937 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java index 95f9d6e..fd0badb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java @@ -39,11 +39,11 @@ import org.apache.logging.log4j.core.net.AbstractSocketManager; import org.apache.logging.log4j.core.net.Advertiser; import org.apache.logging.log4j.core.net.DatagramSocketManager; import org.apache.logging.log4j.core.net.Protocol; +import org.apache.logging.log4j.core.net.SocketOptions; import org.apache.logging.log4j.core.net.SslSocketManager; import org.apache.logging.log4j.core.net.TcpSocketManager; import org.apache.logging.log4j.core.net.ssl.SslConfiguration; import org.apache.logging.log4j.core.util.Booleans; -import org.apache.logging.log4j.core.util.Constants; /** * An Appender that delivers events over socket connections. Supports both TCP and UDP. @@ -88,6 +88,9 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM @PluginAliases({ "reconnectDelay, delayMillis" }) private int reconnectDelayMillis; + @PluginElement("SocketOptions") + private SocketOptions socketOptions; + @PluginElement("SslConfiguration") @PluginAliases({ "SslConfig" }) private SslConfiguration sslConfiguration; @@ -114,7 +117,7 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM } final AbstractSocketManager manager = SocketAppender.createSocketManager(name, actualProtocol, host, port, - connectTimeoutMillis, sslConfiguration, reconnectDelayMillis, immediateFail, layout, getBufferSize()); + connectTimeoutMillis, sslConfiguration, reconnectDelayMillis, immediateFail, layout, getBufferSize(), socketOptions); return new SocketAppender(name, layout, getFilter(), manager, isIgnoreExceptions(), !bufferedIo || immediateFlush, advertise ? configuration.getAdvertiser() : null); @@ -188,6 +191,11 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM return asBuilder(); } + public B withSocketOptions(final SocketOptions socketOptions) { + this.socketOptions = socketOptions; + return asBuilder(); + } + public B withSslConfiguration(final SslConfiguration sslConfiguration) { this.sslConfiguration = sslConfiguration; return asBuilder(); @@ -373,10 +381,25 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM * * @throws IllegalArgumentException * if the protocol cannot be handled. + * @deprecated Use {@link #createSocketManager(String, Protocol, String, int, int, SslConfiguration, int, boolean, Layout, int, SocketOptions)}. */ + @Deprecated protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host, final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, final int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { + return createSocketManager(name, protocol, host, port, connectTimeoutMillis, sslConfig, reconnectDelayMillis, immediateFail, layout, bufferSize, null); + } + + /** + * Creates an AbstractSocketManager for TCP, UDP, and SSL. + * + * @throws IllegalArgumentException + * if the protocol cannot be handled. + */ + protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host, + final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, + final int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, + final int bufferSize, final SocketOptions socketOptions) { if (protocol == Protocol.TCP && sslConfig != null) { // Upgrade TCP to SSL if an SSL config is specified. protocol = Protocol.SSL; @@ -386,13 +409,13 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM } switch (protocol) { case TCP: - return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, - layout, bufferSize); + return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, reconnectDelayMillis, + immediateFail, layout, bufferSize, socketOptions); case UDP: return DatagramSocketManager.getSocketManager(host, port, layout, bufferSize); case SSL: return SslSocketManager.getSocketManager(sslConfig, host, port, connectTimeoutMillis, reconnectDelayMillis, - immediateFail, layout, bufferSize); + immediateFail, layout, bufferSize, socketOptions); default: throw new IllegalArgumentException(protocol.toString()); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java index bfe7e3a..6df591d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java @@ -23,10 +23,12 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.appender.SocketAppender.Builder; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAliases; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; @@ -37,6 +39,7 @@ import org.apache.logging.log4j.core.net.AbstractSocketManager; import org.apache.logging.log4j.core.net.Advertiser; import org.apache.logging.log4j.core.net.Facility; import org.apache.logging.log4j.core.net.Protocol; +import org.apache.logging.log4j.core.net.SocketOptions; import org.apache.logging.log4j.core.net.ssl.SslConfiguration; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.util.EnglishEnums; @@ -47,6 +50,40 @@ import org.apache.logging.log4j.util.EnglishEnums; @Plugin(name = "Syslog", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) public class SyslogAppender extends SocketAppender { + public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B> + implements org.apache.logging.log4j.core.util.Builder<SyslogAppender> { + +// @PluginAliases("reconnectionDelay") // deprecated +// @PluginAttribute("name") final String name, +// @PluginAttribute(value = "immediateFlush", defaultBoolean = true) final boolean immediateFlush, +// @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final boolean ignoreExceptions, +// @PluginAttribute(value = "facility", defaultString = "LOCAL0") final Facility facility, +// @PluginAttribute("id") final String id, +// @PluginAttribute(value = "enterpriseNumber", defaultInt = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER) final int enterpriseNumber, +// @PluginAttribute(value = "includeMdc", defaultBoolean = true) final boolean includeMdc, +// @PluginAttribute("mdcId") final String mdcId, +// @PluginAttribute("mdcPrefix") final String mdcPrefix, +// @PluginAttribute("eventPrefix") final String eventPrefix, +// @PluginAttribute(value = "newLine") final boolean newLine, +// @PluginAttribute("newLineEscape") final String escapeNL, +// @PluginAttribute("appName") final String appName, +// @PluginAttribute("messageId") final String msgId, +// @PluginAttribute("mdcExcludes") final String excludes, +// @PluginAttribute("mdcIncludes") final String includes, +// @PluginAttribute("mdcRequired") final String required, +// @PluginAttribute("format") final String format, +// @PluginElement("Filter") final Filter filter, +// @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charsetName, +// @PluginAttribute("exceptionPattern") final String exceptionPattern, +// @PluginElement("LoggerFields") final LoggerFields[] loggerFields, + + @Override + public SyslogAppender build() { + // TODO Auto-generated method stub + return null; + } + } + protected static final String RFC5424 = "RFC5424"; protected SyslogAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/net/Rfc1349TrafficClass.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/Rfc1349TrafficClass.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/Rfc1349TrafficClass.java new file mode 100644 index 0000000..cb071e4 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/Rfc1349TrafficClass.java @@ -0,0 +1,50 @@ +/* + * 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.net; + +/** + * Enumerates the RFC 1349 TOS field. + * + * <ul> + * <li>1000 -- minimize delay</li> + * <li>0100 -- maximize throughput</li> + * <li>0010 -- maximize reliability</li> + * <li>0001 -- minimize monetary cost</li> + * <li>0000 -- normal service</li> + * <ul> + */ +public enum Rfc1349TrafficClass { + + // @formatter:off + IPTOS_NORMAL(0x00), + IPTOS_LOWCOST(0x02), + IPTOS_LOWDELAY (0x10), + IPTOS_RELIABILITY (0x04), + IPTOS_THROUGHPUT (0x08); + // @formatter:on + + private final int trafficClass; + + private Rfc1349TrafficClass(final int trafficClass) { + this.trafficClass = trafficClass; + } + + public int value() { + return trafficClass; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketOptions.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketOptions.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketOptions.java new file mode 100644 index 0000000..62364e5 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketOptions.java @@ -0,0 +1,222 @@ +/* + * 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.net; + +import java.net.Socket; +import java.net.SocketException; + +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.util.Builder; + +/** + * Holds all socket options settable via {@link Socket} methods. + */ +@Plugin(name = "SocketOptions", category = Core.CATEGORY_NAME, printObject = true) +public class SocketOptions implements Builder<SocketOptions>, Cloneable { + + @PluginBuilderFactory + public static SocketOptions newBuilder() { + return new SocketOptions(); + } + + @PluginBuilderAttribute + private Boolean keepAlive; + + @PluginBuilderAttribute + private Boolean oobInline; + + @PluginElement("PerformancePreferences") + private SocketPerformancePreferences performancePreferences; + + @PluginBuilderAttribute + private Integer receiveBufferSize; + + @PluginBuilderAttribute + private Boolean reuseAddress; + + @PluginBuilderAttribute + private Rfc1349TrafficClass rfc1349TrafficClass; + + @PluginBuilderAttribute + private Integer sendBufferSize; + + @PluginBuilderAttribute + private Integer soLinger; + + @PluginBuilderAttribute + private Integer soTimeout; + + @PluginBuilderAttribute + private Boolean tcpNoDelay; + + @PluginBuilderAttribute + private Integer trafficClass; + + public void apply(final Socket socket) throws SocketException { + if (keepAlive != null) { + socket.setKeepAlive(keepAlive.booleanValue()); + } + if (oobInline != null) { + socket.setOOBInline(oobInline.booleanValue()); + } + if (reuseAddress != null) { + socket.setReuseAddress(reuseAddress.booleanValue()); + } + if (performancePreferences != null) { + performancePreferences.apply(socket); + } + if (receiveBufferSize != null) { + socket.setReceiveBufferSize(receiveBufferSize.intValue()); + } + if (soLinger != null) { + socket.setSoLinger(true, soLinger.intValue()); + } + if (soTimeout != null) { + socket.setSoTimeout(soTimeout.intValue()); + } + if (tcpNoDelay != null) { + socket.setTcpNoDelay(tcpNoDelay.booleanValue()); + } + final Integer actualTrafficClass = getActualTrafficClass(); + if (actualTrafficClass != null) { + socket.setTrafficClass(actualTrafficClass); + } + } + + @Override + public SocketOptions build() { + try { + return (SocketOptions) clone(); + } catch (final CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + + public Integer getActualTrafficClass() { + if (trafficClass != null && rfc1349TrafficClass != null) { + throw new IllegalStateException("You MUST not set both customTrafficClass and trafficClass."); + } + if (trafficClass != null) { + return trafficClass; + } + if (rfc1349TrafficClass != null) { + return Integer.valueOf(rfc1349TrafficClass.value()); + } + return null; + } + + public SocketPerformancePreferences getPerformancePreferences() { + return performancePreferences; + } + + public Integer getReceiveBufferSize() { + return receiveBufferSize; + } + + public Rfc1349TrafficClass getRfc1349TrafficClass() { + return rfc1349TrafficClass; + } + + public Integer getSendBufferSize() { + return sendBufferSize; + } + + public Integer getSoLinger() { + return soLinger; + } + + public Integer getSoTimeout() { + return soTimeout; + } + + public Integer getTrafficClass() { + return trafficClass; + } + + public Boolean isKeepAlive() { + return keepAlive; + } + + public Boolean isOobInline() { + return oobInline; + } + + public Boolean isReuseAddress() { + return reuseAddress; + } + + public Boolean isTcpNoDelay() { + return tcpNoDelay; + } + + public void setKeepAlive(final boolean keepAlive) { + this.keepAlive = Boolean.valueOf(keepAlive); + } + + public void setOobInline(final boolean oobInline) { + this.oobInline = Boolean.valueOf(oobInline); + } + + public void setPerformancePreferences(final SocketPerformancePreferences performancePreferences) { + this.performancePreferences = performancePreferences; + } + + public void setReceiveBufferSize(final int receiveBufferSize) { + this.receiveBufferSize = receiveBufferSize; + } + + public void setReuseAddress(final boolean reuseAddress) { + this.reuseAddress = Boolean.valueOf(reuseAddress); + } + + public void setRfc1349TrafficClass(final Rfc1349TrafficClass trafficClass) { + this.rfc1349TrafficClass = trafficClass; + } + + public void setSendBufferSize(final int sendBufferSize) { + this.sendBufferSize = sendBufferSize; + } + + public void setSoLinger(final int soLinger) { + this.soLinger = soLinger; + } + + public void setSoTimeout(final int soTimeout) { + this.soTimeout = soTimeout; + } + + public void setTcpNoDelay(final boolean tcpNoDelay) { + this.tcpNoDelay = Boolean.valueOf(tcpNoDelay); + } + + public void setTrafficClass(final int trafficClass) { + this.trafficClass = trafficClass; + } + + @Override + public String toString() { + return "SocketOptions [keepAlive=" + keepAlive + ", oobInline=" + oobInline + ", performancePreferences=" + + performancePreferences + ", receiveBufferSize=" + receiveBufferSize + ", reuseAddress=" + reuseAddress + + ", rfc1349TrafficClass=" + rfc1349TrafficClass + ", sendBufferSize=" + sendBufferSize + ", soLinger=" + + soLinger + ", soTimeout=" + soTimeout + ", tcpNoDelay=" + tcpNoDelay + ", trafficClass=" + + trafficClass + "]"; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketPerformancePreferences.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketPerformancePreferences.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketPerformancePreferences.java new file mode 100644 index 0000000..c7a246a --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SocketPerformancePreferences.java @@ -0,0 +1,82 @@ +package org.apache.logging.log4j.core.net; + +import java.net.Socket; +import java.net.SocketException; + +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.util.Builder; + +/** + * Holds all socket options settable via {@link Socket#setPerformancePreferences(int, int, int)}. + * <p> + * The {@link Socket#setPerformancePreferences(int, int, int)} API may not be implemented by a JRE. + * </p> + */ +@Plugin(name = "SocketPerformancePreferences", category = Core.CATEGORY_NAME, printObject = true) +public class SocketPerformancePreferences implements Builder<SocketPerformancePreferences>, Cloneable { + + @PluginBuilderFactory + public static SocketPerformancePreferences newBuilder() { + return new SocketPerformancePreferences(); + } + + @PluginBuilderAttribute + @Required + private int bandwidth; + + @PluginBuilderAttribute + @Required + private int connectionTime; + + @PluginBuilderAttribute + @Required + private int latency; + + public void apply(final Socket socket) { + socket.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + @Override + public SocketPerformancePreferences build() { + try { + return (SocketPerformancePreferences) clone(); + } catch (final CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + + public int getBandwidth() { + return bandwidth; + } + + public int getConnectionTime() { + return connectionTime; + } + + public int getLatency() { + return latency; + } + + public void setBandwidth(final int bandwidth) { + this.bandwidth = bandwidth; + } + + public void setConnectionTime(final int connectionTime) { + this.connectionTime = connectionTime; + } + + public void setLatency(final int latency) { + this.latency = latency; + } + + @Override + public String toString() { + return "SocketPerformancePreferences [bandwidth=" + bandwidth + ", connectionTime=" + connectionTime + + ", latency=" + latency + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java index ec5c64a..c9d5c6e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SslSocketManager.java @@ -43,27 +43,51 @@ public class SslSocketManager extends TcpSocketManager { private final SslConfiguration sslConfig; /** - * - * - * @param name The unique name of this connection. - * @param os The OutputStream. - * @param sock The Socket. - * @param inetAddress The Internet address of the host. - * @param host The name of the host. - * @param port The port number on the host. - * @param connectTimeoutMillis the connect timeout in milliseconds. - * @param delay Reconnection interval. - * @param immediateFail - * @param layout The Layout. - * @param bufferSize The buffer size. - */ - public SslSocketManager(final String name, final OutputStream os, final Socket sock, - final SslConfiguration sslConfig, final InetAddress inetAddress, final String host, final int port, - final int connectTimeoutMillis, final int delay, final boolean immediateFail, - final Layout<? extends Serializable> layout, final int bufferSize) { - super(name, os, sock, inetAddress, host, port, connectTimeoutMillis, delay, immediateFail, layout, bufferSize); - this.sslConfig = sslConfig; - } + * + * + * @param name The unique name of this connection. + * @param os The OutputStream. + * @param sock The Socket. + * @param inetAddress The Internet address of the host. + * @param host The name of the host. + * @param port The port number on the host. + * @param connectTimeoutMillis the connect timeout in milliseconds. + * @param delay Reconnection interval. + * @param immediateFail + * @param layout The Layout. + * @param bufferSize The buffer size. + * @deprecated Use {@link #SslSocketManager(String, OutputStream, Socket, SslConfiguration, InetAddress, String, int, int, int, boolean, Layout, int, SocketOptions)}. + */ + public SslSocketManager(final String name, final OutputStream os, final Socket sock, + final SslConfiguration sslConfig, final InetAddress inetAddress, final String host, final int port, + final int connectTimeoutMillis, final int delay, final boolean immediateFail, + final Layout<? extends Serializable> layout, final int bufferSize) { + super(name, os, sock, inetAddress, host, port, connectTimeoutMillis, delay, immediateFail, layout, bufferSize, null); + this.sslConfig = sslConfig; + } + + /** + * + * + * @param name The unique name of this connection. + * @param os The OutputStream. + * @param sock The Socket. + * @param inetAddress The Internet address of the host. + * @param host The name of the host. + * @param port The port number on the host. + * @param connectTimeoutMillis the connect timeout in milliseconds. + * @param delay Reconnection interval. + * @param immediateFail + * @param layout The Layout. + * @param bufferSize The buffer size. + */ + public SslSocketManager(final String name, final OutputStream os, final Socket sock, + final SslConfiguration sslConfig, final InetAddress inetAddress, final String host, final int port, + final int connectTimeoutMillis, final int delay, final boolean immediateFail, + final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) { + super(name, os, sock, inetAddress, host, port, connectTimeoutMillis, delay, immediateFail, layout, bufferSize, socketOptions); + this.sslConfig = sslConfig; + } private static class SslFactoryData { protected SslConfiguration sslConfiguration; @@ -74,10 +98,11 @@ public class SslSocketManager extends TcpSocketManager { private final boolean immediateFail; private final Layout<? extends Serializable> layout; private final int bufferSize; + private final SocketOptions socketOptions; public SslFactoryData(final SslConfiguration sslConfiguration, final String host, final int port, final int connectTimeoutMillis, final int delayMillis, final boolean immediateFail, - final Layout<? extends Serializable> layout, final int bufferSize) { + final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) { this.host = host; this.port = port; this.connectTimeoutMillis = connectTimeoutMillis; @@ -86,12 +111,23 @@ public class SslSocketManager extends TcpSocketManager { this.layout = layout; this.sslConfiguration = sslConfiguration; this.bufferSize = bufferSize; + this.socketOptions = socketOptions; } } + /** + * @deprecated Use {@link SslSocketManager#getSocketManager(SslConfiguration, String, int, int, int, boolean, Layout, int, SocketOptions)}. + */ + @Deprecated public static SslSocketManager getSocketManager(final SslConfiguration sslConfig, final String host, int port, final int connectTimeoutMillis, int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { + return getSocketManager(sslConfig, host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize, null); + } + + public static SslSocketManager getSocketManager(final SslConfiguration sslConfig, final String host, int port, + final int connectTimeoutMillis, int reconnectDelayMillis, final boolean immediateFail, + final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) { if (Strings.isEmpty(host)) { throw new IllegalArgumentException("A host name is required"); } @@ -102,7 +138,7 @@ public class SslSocketManager extends TcpSocketManager { reconnectDelayMillis = DEFAULT_RECONNECTION_DELAY_MILLIS; } return (SslSocketManager) getManager("TLS:" + host + ':' + port, new SslFactoryData(sslConfig, host, port, - connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize), FACTORY); + connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize, socketOptions), FACTORY); } @Override @@ -155,7 +191,7 @@ public class SslSocketManager extends TcpSocketManager { return null; } return new SslSocketManager(name, os, socket, data.sslConfiguration, inetAddress, data.host, data.port, 0, - data.delayMillis, data.immediateFail, data.layout, data.bufferSize); + data.delayMillis, data.immediateFail, data.layout, data.bufferSize, data.socketOptions); } private InetAddress resolveAddress(final String hostName) throws TlsSocketManagerFactoryException { @@ -183,6 +219,10 @@ public class SslSocketManager extends TcpSocketManager { socketFactory = createSslSocketFactory(data.sslConfiguration); socket = (SSLSocket) socketFactory.createSocket(data.host, data.port); + final SocketOptions socketOptions = data.socketOptions; + if (socketOptions != null) { + socketOptions.apply(socket); + } return socket; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java index 1e43cee..d7d210e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java @@ -23,6 +23,7 @@ import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; @@ -32,6 +33,7 @@ import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.appender.AppenderLoggingException; import org.apache.logging.log4j.core.appender.ManagerFactory; import org.apache.logging.log4j.core.appender.OutputStreamManager; +import org.apache.logging.log4j.core.net.TcpSocketManager.TcpSocketManagerFactory; import org.apache.logging.log4j.core.util.Log4jThread; import org.apache.logging.log4j.core.util.NullOutputStream; import org.apache.logging.log4j.util.Strings; @@ -41,11 +43,11 @@ import org.apache.logging.log4j.util.Strings; */ public class TcpSocketManager extends AbstractSocketManager { /** - The default reconnection delay (30000 milliseconds or 30 seconds). + * The default reconnection delay (30000 milliseconds or 30 seconds). */ - public static final int DEFAULT_RECONNECTION_DELAY_MILLIS = 30000; + public static final int DEFAULT_RECONNECTION_DELAY_MILLIS = 30000; /** - The default port number of remote logging server (4560). + * The default port number of remote logging server (4560). */ private static final int DEFAULT_PORT = 4560; @@ -57,29 +59,81 @@ public class TcpSocketManager extends AbstractSocketManager { private Socket socket; + private final SocketOptions socketOptions; + private final boolean retry; private final boolean immediateFail; - + private final int connectTimeoutMillis; /** - * The Constructor. - * @param name The unique name of this connection. - * @param os The OutputStream. - * @param socket The Socket. - * @param inetAddress The Internet address of the host. - * @param host The name of the host. - * @param port The port number on the host. - * @param connectTimeoutMillis the connect timeout in milliseconds. - * @param delay Reconnection interval. - * @param immediateFail True if the write should fail if no socket is immediately available. - * @param layout The Layout. - * @param bufferSize The buffer size. + * Constructs. + * + * @param name + * The unique name of this connection. + * @param os + * The OutputStream. + * @param socket + * The Socket. + * @param inetAddress + * The Internet address of the host. + * @param host + * The name of the host. + * @param port + * The port number on the host. + * @param connectTimeoutMillis + * the connect timeout in milliseconds. + * @param delay + * Reconnection interval. + * @param immediateFail + * True if the write should fail if no socket is immediately available. + * @param layout + * The Layout. + * @param bufferSize + * The buffer size. + * @deprecated Use + * {@link TcpSocketManager#TcpSocketManager(String, OutputStream, Socket, InetAddress, String, int, int, int, boolean, Layout, int, SocketOptions)}. + */ + @Deprecated + public TcpSocketManager(final String name, final OutputStream os, final Socket socket, + final InetAddress inetAddress, final String host, final int port, final int connectTimeoutMillis, + final int delay, final boolean immediateFail, final Layout<? extends Serializable> layout, + final int bufferSize) { + this(name, os, socket, inetAddress, host, port, connectTimeoutMillis, delay, immediateFail, layout, bufferSize, + null); + } + + /** + * Constructs. + * + * @param name + * The unique name of this connection. + * @param os + * The OutputStream. + * @param socket + * The Socket. + * @param inetAddress + * The Internet address of the host. + * @param host + * The name of the host. + * @param port + * The port number on the host. + * @param connectTimeoutMillis + * the connect timeout in milliseconds. + * @param delay + * Reconnection interval. + * @param immediateFail + * True if the write should fail if no socket is immediately available. + * @param layout + * The Layout. + * @param bufferSize + * The buffer size. */ - public TcpSocketManager(final String name, final OutputStream os, final Socket socket, final InetAddress inetAddress, - final String host, final int port, final int connectTimeoutMillis, final int delay, - final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { + public TcpSocketManager(final String name, final OutputStream os, final Socket socket, + final InetAddress inetAddress, final String host, final int port, final int connectTimeoutMillis, + final int delay, final boolean immediateFail, final Layout<? extends Serializable> layout, + final int bufferSize, final SocketOptions socketOptions) { super(name, os, inetAddress, host, port, layout, true, bufferSize); this.connectTimeoutMillis = connectTimeoutMillis; this.reconnectionDelay = delay; @@ -90,19 +144,51 @@ public class TcpSocketManager extends AbstractSocketManager { reconnector = createReconnector(); reconnector.start(); } + this.socketOptions = socketOptions; + } + + /** + * Obtains a TcpSocketManager. + * + * @param host + * The host to connect to. + * @param port + * The port on the host. + * @param connectTimeoutMillis + * the connect timeout in milliseconds + * @param reconnectDelayMillis + * The interval to pause between retries. + * @param bufferSize + * The buffer size. + * @return A TcpSocketManager. + * @deprecated Use {@link #getSocketManager(String, int, int, int, boolean, Layout, int, SocketOptions)}. + */ + @Deprecated + public static TcpSocketManager getSocketManager(final String host, int port, final int connectTimeoutMillis, + int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, + final int bufferSize) { + return getSocketManager(host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, + bufferSize, null); } /** * Obtains a TcpSocketManager. - * @param host The host to connect to. - * @param port The port on the host. - * @param connectTimeoutMillis the connect timeout in milliseconds - * @param reconnectDelayMillis The interval to pause between retries. - * @param bufferSize The buffer size. + * + * @param host + * The host to connect to. + * @param port + * The port on the host. + * @param connectTimeoutMillis + * the connect timeout in milliseconds + * @param reconnectDelayMillis + * The interval to pause between retries. + * @param bufferSize + * The buffer size. * @return A TcpSocketManager. */ public static TcpSocketManager getSocketManager(final String host, int port, final int connectTimeoutMillis, - int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { + int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, + final int bufferSize, final SocketOptions socketOptions) { if (Strings.isEmpty(host)) { throw new IllegalArgumentException("A host name is required"); } @@ -112,12 +198,12 @@ public class TcpSocketManager extends AbstractSocketManager { if (reconnectDelayMillis == 0) { reconnectDelayMillis = DEFAULT_RECONNECTION_DELAY_MILLIS; } - return (TcpSocketManager) getManager("TCP:" + host + ':' + port, new FactoryData( - host, port, connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize), FACTORY); + return (TcpSocketManager) getManager("TCP:" + host + ':' + port, new FactoryData(host, port, + connectTimeoutMillis, reconnectDelayMillis, immediateFail, layout, bufferSize, socketOptions), FACTORY); } @Override - protected void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) { + protected void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) { if (socket == null) { if (reconnector != null && !immediateFail) { reconnector.latch(); @@ -203,7 +289,7 @@ public class TcpSocketManager extends AbstractSocketManager { this.owner = owner; } - public void latch() { + public void latch() { try { latch.await(); } catch (final InterruptedException ex) { @@ -260,9 +346,11 @@ public class TcpSocketManager extends AbstractSocketManager { } protected Socket createSocket(final String host, final int port) throws IOException { - final InetSocketAddress address = new InetSocketAddress(host, port); final Socket newSocket = new Socket(); - newSocket.connect(address, connectTimeoutMillis); + newSocket.connect(new InetSocketAddress(host, port), connectTimeoutMillis); + if (socketOptions != null) { + socketOptions.apply(newSocket); + } return newSocket; } @@ -277,9 +365,11 @@ public class TcpSocketManager extends AbstractSocketManager { private final boolean immediateFail; private final Layout<? extends Serializable> layout; private final int bufferSize; + private final SocketOptions socketOptions; - public FactoryData(final String host, final int port, final int connectTimeoutMillis, final int reconnectDelayMillis, - final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { + public FactoryData(final String host, final int port, final int connectTimeoutMillis, + final int reconnectDelayMillis, final boolean immediateFail, + final Layout<? extends Serializable> layout, final int bufferSize, final SocketOptions socketOptions) { this.host = host; this.port = port; this.connectTimeoutMillis = connectTimeoutMillis; @@ -287,6 +377,7 @@ public class TcpSocketManager extends AbstractSocketManager { this.immediateFail = immediateFail; this.layout = layout; this.bufferSize = bufferSize; + this.socketOptions = socketOptions; } } @@ -307,11 +398,11 @@ public class TcpSocketManager extends AbstractSocketManager { } try { // LOG4J2-1042 - final Socket socket = new Socket(); - socket.connect(new InetSocketAddress(data.host, data.port), data.connectTimeoutMillis); + final Socket socket = createSocket(data); os = socket.getOutputStream(); return new TcpSocketManager(name, os, socket, inetAddress, data.host, data.port, - data.connectTimeoutMillis, data.reconnectDelayMillis, data.immediateFail, data.layout, data.bufferSize); + data.connectTimeoutMillis, data.reconnectDelayMillis, data.immediateFail, data.layout, + data.bufferSize, data.socketOptions); } catch (final IOException ex) { LOGGER.error("TcpSocketManager (" + name + ") " + ex, ex); os = NullOutputStream.getInstance(); @@ -320,8 +411,33 @@ public class TcpSocketManager extends AbstractSocketManager { return null; } return new TcpSocketManager(name, os, null, inetAddress, data.host, data.port, data.connectTimeoutMillis, - data.reconnectDelayMillis, data.immediateFail, data.layout, data.bufferSize); + data.reconnectDelayMillis, data.immediateFail, data.layout, data.bufferSize, data.socketOptions); + } + + static Socket createSocket(final FactoryData data) throws IOException, SocketException { + final Socket socket = new Socket(); + socket.connect(new InetSocketAddress(data.host, data.port), data.connectTimeoutMillis); + final SocketOptions socketOptions = data.socketOptions; + if (socketOptions != null) { + socketOptions.apply(socket); + } + return socket; } + + } + + /** + * USE AT YOUR OWN RISK, method is public for testing purpose only for now. + */ + public SocketOptions getSocketOptions() { + return socketOptions; + } + + /** + * USE AT YOUR OWN RISK, method is public for testing purpose only for now. + */ + public Socket getSocket() { + return socket; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SecureSocketAppenderSocketOptionsTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SecureSocketAppenderSocketOptionsTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SecureSocketAppenderSocketOptionsTest.java new file mode 100644 index 0000000..cc1327f --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SecureSocketAppenderSocketOptionsTest.java @@ -0,0 +1,119 @@ +/* + * 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 java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; + +import javax.net.ssl.SSLServerSocketFactory; + +import org.apache.logging.log4j.core.appender.SocketAppenderTest.TcpSocketTestServer; +import org.apache.logging.log4j.core.net.Rfc1349TrafficClass; +import org.apache.logging.log4j.core.net.SocketOptions; +import org.apache.logging.log4j.core.net.TcpSocketManager; +import org.apache.logging.log4j.core.net.ssl.KeyStoreConfiguration; +import org.apache.logging.log4j.core.net.ssl.SslConfiguration; +import org.apache.logging.log4j.core.net.ssl.StoreConfigurationException; +import org.apache.logging.log4j.core.net.ssl.TestConstants; +import org.apache.logging.log4j.core.net.ssl.TrustStoreConfiguration; +import org.apache.logging.log4j.core.util.NullOutputStream; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.AvailablePortFinder; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +public class SecureSocketAppenderSocketOptionsTest { + + private static final int PORT; + private static TcpSocketTestServer tcpSocketTestServer; + + private static SSLServerSocketFactory serverSocketFactory; + private static SslConfiguration sslConfiguration; + + static { + PORT = AvailablePortFinder.getNextAvailable(); + System.setProperty("SecureSocketAppenderSocketOptionsTest.port", Integer.toString(PORT)); + try { + initServerSocketFactory(); + tcpSocketTestServer = new TcpSocketTestServer(serverSocketFactory.createServerSocket(PORT)); + tcpSocketTestServer.start(); + loggerContextRule = new LoggerContextRule("log4j-ssl-socket-options.xml"); + } catch (IOException | StoreConfigurationException e) { + throw new IllegalStateException(e); + } + + } + + @ClassRule + public static final LoggerContextRule loggerContextRule; + + @AfterClass + public static void afterClass() { + if (tcpSocketTestServer != null) { + tcpSocketTestServer.shutdown(); + } + } + + public static void initServerSocketFactory() throws StoreConfigurationException { + final KeyStoreConfiguration ksc = new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE, + TestConstants.KEYSTORE_PWD, null, null); + final TrustStoreConfiguration tsc = new TrustStoreConfiguration(TestConstants.TRUSTSTORE_FILE, + TestConstants.TRUSTSTORE_PWD, null, null); + sslConfiguration = SslConfiguration.createSSLConfiguration(null, ksc, tsc); + serverSocketFactory = sslConfiguration.getSslServerSocketFactory(); + } + + @Test + public void testSocketOptions() throws IOException { + Assert.assertNotNull(loggerContextRule); + Assert.assertNotNull(loggerContextRule.getConfiguration()); + final SocketAppender appender = loggerContextRule.getAppender("socket", SocketAppender.class); + Assert.assertNotNull(appender); + final TcpSocketManager manager = (TcpSocketManager) appender.getManager(); + Assert.assertNotNull(manager); + final OutputStream outputStream = manager.getOutputStream(); + Assert.assertFalse(outputStream instanceof NullOutputStream); + final SocketOptions socketOptions = manager.getSocketOptions(); + Assert.assertNotNull(socketOptions); + final Socket socket = manager.getSocket(); + Assert.assertNotNull(socket); + // Test config request + Assert.assertEquals(false, socketOptions.isKeepAlive()); + Assert.assertEquals(null, socketOptions.isOobInline()); + Assert.assertEquals(false, socketOptions.isReuseAddress()); + Assert.assertEquals(false, socketOptions.isTcpNoDelay()); + Assert.assertEquals(Rfc1349TrafficClass.IPTOS_LOWCOST.value(), + socketOptions.getActualTrafficClass().intValue()); + Assert.assertEquals(10000, socketOptions.getReceiveBufferSize().intValue()); + Assert.assertEquals(8000, socketOptions.getSendBufferSize().intValue()); + Assert.assertEquals(12345, socketOptions.getSoLinger().intValue()); + Assert.assertEquals(54321, socketOptions.getSoTimeout().intValue()); + // Test live socket + Assert.assertEquals(false, socket.getKeepAlive()); + Assert.assertEquals(false, socket.getReuseAddress()); + Assert.assertEquals(false, socket.getTcpNoDelay()); + Assert.assertEquals(Rfc1349TrafficClass.IPTOS_LOWCOST.value(), socket.getTrafficClass()); + Assert.assertEquals(10000, socket.getReceiveBufferSize()); + // This settings changes while we are running, so we cannot assert it. + // Assert.assertEquals(8000, socket.getSendBufferSize()); + Assert.assertEquals(12345, socket.getSoLinger()); + Assert.assertEquals(54321, socket.getSoTimeout()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSocketOptionsTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSocketOptionsTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSocketOptionsTest.java new file mode 100644 index 0000000..ea3ada1 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSocketOptionsTest.java @@ -0,0 +1,99 @@ +/* + * 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 java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; + +import org.apache.logging.log4j.core.appender.SocketAppenderTest.TcpSocketTestServer; +import org.apache.logging.log4j.core.net.Rfc1349TrafficClass; +import org.apache.logging.log4j.core.net.SocketOptions; +import org.apache.logging.log4j.core.net.TcpSocketManager; +import org.apache.logging.log4j.core.util.NullOutputStream; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.AvailablePortFinder; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +public class SocketAppenderSocketOptionsTest { + + private static final int PORT; + private static TcpSocketTestServer tcpSocketTestServer; + + static { + PORT = AvailablePortFinder.getNextAvailable(); + System.setProperty("SocketAppenderSocketOptionsTest.port", Integer.toString(PORT)); + try { + tcpSocketTestServer = new TcpSocketTestServer(PORT); + } catch (IOException e) { + throw new IllegalStateException(e); + } + tcpSocketTestServer.start(); + loggerContextRule = new LoggerContextRule("log4j-socket-options.xml"); + } + + @ClassRule + public static final LoggerContextRule loggerContextRule; + + @AfterClass + public static void afterClass() { + if (tcpSocketTestServer != null) { + tcpSocketTestServer.shutdown(); + } + } + + @Test + public void testSocketOptions() throws IOException { + Assert.assertNotNull(loggerContextRule); + Assert.assertNotNull(loggerContextRule.getConfiguration()); + final SocketAppender appender = loggerContextRule.getAppender("socket", SocketAppender.class); + Assert.assertNotNull(appender); + final TcpSocketManager manager = (TcpSocketManager) appender.getManager(); + Assert.assertNotNull(manager); + final OutputStream outputStream = manager.getOutputStream(); + Assert.assertFalse(outputStream instanceof NullOutputStream); + final SocketOptions socketOptions = manager.getSocketOptions(); + Assert.assertNotNull(socketOptions); + final Socket socket = manager.getSocket(); + Assert.assertNotNull(socket); + // Test config request + Assert.assertEquals(false, socketOptions.isKeepAlive()); + Assert.assertEquals(false, socketOptions.isOobInline()); + Assert.assertEquals(false, socketOptions.isReuseAddress()); + Assert.assertEquals(false, socketOptions.isTcpNoDelay()); + Assert.assertEquals(Rfc1349TrafficClass.IPTOS_LOWCOST.value(), + socketOptions.getActualTrafficClass().intValue()); + Assert.assertEquals(10000, socketOptions.getReceiveBufferSize().intValue()); + Assert.assertEquals(8000, socketOptions.getSendBufferSize().intValue()); + Assert.assertEquals(12345, socketOptions.getSoLinger().intValue()); + Assert.assertEquals(54321, socketOptions.getSoTimeout().intValue()); + // Test live socket + Assert.assertEquals(false, socket.getKeepAlive()); + Assert.assertEquals(false, socket.getOOBInline()); + Assert.assertEquals(false, socket.getReuseAddress()); + Assert.assertEquals(false, socket.getTcpNoDelay()); + Assert.assertEquals(Rfc1349TrafficClass.IPTOS_LOWCOST.value(), socket.getTrafficClass()); + Assert.assertEquals(10000, socket.getReceiveBufferSize()); + // This settings changes while we are running, so we cannot assert it. + // Assert.assertEquals(8000, socket.getSendBufferSize()); + Assert.assertEquals(12345, socket.getSoLinger()); + Assert.assertEquals(54321, socket.getSoTimeout()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderTest.java index 8d7e4b1..e8d1053 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderTest.java @@ -332,8 +332,13 @@ public class SocketAppenderTest { private volatile int count = 0; private final BlockingQueue<LogEvent> queue; + @SuppressWarnings("resource") public TcpSocketTestServer(final int port) throws IOException { - this.serverSocket = new ServerSocket(port); + this(new ServerSocket(port)); + } + + public TcpSocketTestServer(ServerSocket serverSocket) { + this.serverSocket = serverSocket; this.queue = new ArrayBlockingQueue<>(10); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/test/resources/log4j-socket-options.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-socket-options.xml b/log4j-core/src/test/resources/log4j-socket-options.xml new file mode 100644 index 0000000..3af324f --- /dev/null +++ b/log4j-core/src/test/resources/log4j-socket-options.xml @@ -0,0 +1,32 @@ +<?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. + --> +<Configuration status="OFF" name="MyApp"> + <Appenders> + <Socket name="socket" host="localhost" port="${sys:SocketAppenderSocketOptionsTest.port}" protocol="TCP" ignoreExceptions="false"> + <SocketOptions keepAlive="false" oobInline="false" receiveBufferSize="10000" reuseAddress="false" + rfc1349TrafficClass="IPTOS_LOWCOST" sendBufferSize="8000" soLinger="12345" soTimeout="54321" tcpNoDelay="false"> + <SocketPerformancePreferences bandwidth="100" connectionTime="100" latency="100" /> + </SocketOptions> + </Socket> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="socket" /> + </Root> + </Loggers> +</Configuration> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/log4j-core/src/test/resources/log4j-ssl-socket-options.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-ssl-socket-options.xml b/log4j-core/src/test/resources/log4j-ssl-socket-options.xml new file mode 100644 index 0000000..5b7d08d --- /dev/null +++ b/log4j-core/src/test/resources/log4j-ssl-socket-options.xml @@ -0,0 +1,39 @@ +<?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. + --> +<Configuration status="OFF" name="MyApp"> + <Appenders> + <Socket name="socket" host="localhost" port="${sys:SecureSocketAppenderSocketOptionsTest.port}" protocol="SSL" + ignoreExceptions="false"> + <SocketOptions keepAlive="false" receiveBufferSize="10000" reuseAddress="false" rfc1349TrafficClass="IPTOS_LOWCOST" + sendBufferSize="8000" soLinger="12345" soTimeout="54321" tcpNoDelay="false"> + <SocketPerformancePreferences bandwidth="100" connectionTime="100" latency="100" /> + </SocketOptions> + <Ssl> + <KeyStore location="src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks" + password="changeit" type="JKS" /> + <TrustStore location="src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks" + password="changeit" type="JKS" /> + </Ssl> + </Socket> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="socket" /> + </Root> + </Loggers> +</Configuration> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/92f1c652/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b9aa337..037f404 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -147,6 +147,12 @@ <action issue="LOG4J2-1704" dev="ggregory" type="add"> Add a Builder to RollingRandomAccessFileAppender and deprecate RollingRandomAccessFileAppender.createAppender(). </action> + <action issue="LOG4J2-1707" dev="ggregory" type="add"> + Allow TCP Socket Appender to set socket options. + </action> + <action issue="LOG4J2-1708" dev="ggregory" type="add"> + Allow Secure Socket Appender to set socket options. + </action> </release> <release version="2.7" date="2016-10-02" description="GA Release 2.7"> <action issue="LOG4J2-1618" dev="rpopma" type="fix" due-to="Raman Gupta">
