[
https://issues.apache.org/jira/browse/LOG4J2-895?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14214982#comment-14214982
]
Sam Beroz commented on LOG4J2-895:
----------------------------------
That shouldn't be hard. I just needed a more immediate fix. - Sam
> Specify the SyslogAppender timeout value as part of the configuration
> ---------------------------------------------------------------------
>
> Key: LOG4J2-895
> URL: https://issues.apache.org/jira/browse/LOG4J2-895
> Project: Log4j 2
> Issue Type: New Feature
> Affects Versions: 2.1
> Environment: All
> Reporter: Sam Beroz
> Assignee: Gary Gregory
> Priority: Minor
>
> I was testing the use case of taking the logserver down for maintenance and
> noticed that the SyslogAppender doesn't provide a way to override the default
> socket timeout. This resulted in my application noticeably hanging on
> startup when waiting for the socket to timeout.
> As a short term fix I created an extension largely based on the
> SyslogAppender that includes the connection timeout as a configurable
> parameter (timeoutMillis). I'd like something like this to be added in a
> future revision so I don't have to maintain this extension. Here's my
> version:
> {code:title=FailFastSyslogAppender.java|borderStyle=solid}
> import java.io.ByteArrayOutputStream;
> import java.io.IOException;
> import java.io.OutputStream;
> import java.io.Serializable;
> import java.net.InetAddress;
> import java.net.InetSocketAddress;
> import java.net.Socket;
> import java.net.UnknownHostException;
> import java.nio.charset.Charset;
> import javax.net.ssl.SSLSocket;
> import javax.net.ssl.SSLSocketFactory;
> import org.apache.logging.log4j.Level;
> import org.apache.logging.log4j.core.Filter;
> import org.apache.logging.log4j.core.Layout;
> import org.apache.logging.log4j.core.appender.ManagerFactory;
> import org.apache.logging.log4j.core.appender.SocketAppender;
> 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.PluginConfiguration;
> import org.apache.logging.log4j.core.config.plugins.PluginElement;
> import org.apache.logging.log4j.core.config.plugins.PluginFactory;
> import org.apache.logging.log4j.core.layout.LoggerFields;
> import org.apache.logging.log4j.core.layout.Rfc5424Layout;
> import org.apache.logging.log4j.core.layout.SyslogLayout;
> 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.SslSocketManager;
> import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
> import org.apache.logging.log4j.util.EnglishEnums;
> /**
> * The FailFastSyslog Appender.
> * I created this because the Syslog Appender doesn't currently allow you to
> set a connection timeout value.
> * Other then allowing a timeout it's basically identical to the SSL Syslog
> Appender.
> */
> @Plugin(name = "FailFastSyslog", category = "Core", elementType = "appender",
> printObject = true)
> public class FailFastSyslogAppender extends SocketAppender {
> private static final long serialVersionUID = 1L;
> protected static final String RFC5424 = "RFC5424";
> protected FailFastSyslogAppender(final String name, final Layout<?
> extends Serializable> layout, final Filter filter,
> final boolean ignoreExceptions, final boolean
> immediateFlush,
> final AbstractSocketManager manager, final
> Advertiser advertiser) {
> super(name, layout, filter, manager, ignoreExceptions,
> immediateFlush, advertiser);
> }
> /**
> * Create a FailFastSyslogAppender.
> * @param host The name of the host to connect to.
> * @param port The port to connect to on the target host.
> * @param protocolStr The Protocol to use.
> * @param sslConfig TODO
> * @param reconnectionDelayMillis The interval in which failed writes
> should be retried.
> * @param immediateFail True if the write should fail if no socket is
> immediately available.
> * @param name The name of the Appender.
> * @param immediateFlush "true" if data should be flushed on each write.
> * @param ignoreExceptions If {@code "true"} (default) exceptions
> encountered when appending events are logged;
> * otherwise they are propagated to the caller.
> * @param facility The Facility is used to try to classify the message.
> * @param id The default structured data id to use when formatting
> according to RFC 5424.
> * @param enterpriseNumber The IANA enterprise number.
> * @param includeMdc Indicates whether data from the ThreadContextMap
> will be included in the RFC 5424 Syslog
> * record. Defaults to "true:.
> * @param mdcId The id to use for the MDC Structured Data Element.
> * @param mdcPrefix The prefix to add to MDC key names.
> * @param eventPrefix The prefix to add to event key names.
> * @param newLine If true, a newline will be appended to the end of the
> syslog record. The default is false.
> * @param escapeNL String that should be used to replace newlines within
> the message text.
> * @param appName The value to use as the APP-NAME in the RFC 5424 syslog
> record.
> * @param msgId The default value to be used in the MSGID field of RFC
> 5424 syslog records.
> * @param excludes A comma separated list of mdc keys that should be
> excluded from the LogEvent.
> * @param includes A comma separated list of mdc keys that should be
> included in the FlumeEvent.
> * @param required A comma separated list of mdc keys that must be
> present in the MDC.
> * @param format If set to "RFC5424" the data will be formatted in
> accordance with RFC 5424. Otherwise,
> * it will be formatted as a BSD Syslog record.
> * @param filter A Filter to determine if the event should be handled by
> this Appender.
> * @param config The Configuration.
> * @param charsetName The character set to use when converting the syslog
> String to a byte array.
> * @param exceptionPattern The converter pattern to use for formatting
> exceptions.
> * @param loggerFields The logger fields
> * @param advertise Whether to advertise
> * @return A FailFastSyslogAppender.
> */
> @PluginFactory
> public static FailFastSyslogAppender createAppender(
> // @formatter:off
> @PluginAttribute("host") final String host,
> @PluginAttribute(value = "port", defaultInt = 0) final int port,
> @PluginAttribute("protocol") final String protocolStr,
> @PluginElement("SSL") final SslConfiguration sslConfig,
> @PluginAliases("reconnectionDelay") // deprecated
> @PluginAttribute(value = "reconnectionDelayMillis", defaultInt =
> 0) final int reconnectionDelayMillis,
> @PluginAttribute(value = "timeoutMillis", defaultInt = 2000)
> final int timeoutMillis,
> @PluginAttribute(value = "immediateFail", defaultBoolean = true)
> final boolean immediateFail,
> @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", defaultBoolean = false) 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,
> @PluginConfiguration final Configuration config,
> @PluginAttribute(value = "charset", defaultString = "UTF-8")
> final Charset charsetName,
> @PluginAttribute("exceptionPattern") final String
> exceptionPattern,
> @PluginElement("LoggerFields") final LoggerFields[] loggerFields,
> @PluginAttribute(value = "advertise", defaultBoolean = false)
> final boolean advertise) {
> // @formatter:on
> // TODO: add Protocol to TypeConverters
> final Protocol protocol = EnglishEnums.valueOf(Protocol.class,
> protocolStr);
> final boolean useTlsMessageFormat = sslConfig != null || protocol ==
> Protocol.SSL;
> final Layout<? extends Serializable> layout =
> RFC5424.equalsIgnoreCase(format) ?
> Rfc5424Layout.createLayout(facility, id, enterpriseNumber,
> includeMdc, mdcId, mdcPrefix, eventPrefix, newLine,
> escapeNL, appName, msgId, excludes, includes, required,
> exceptionPattern, useTlsMessageFormat, loggerFields,
> config) :
> SyslogLayout.createLayout(facility, newLine, escapeNL,
> charsetName);
> if (name == null) {
> LOGGER.error("No name provided for FailFastSyslogAppender");
> return null;
> }
>
> SslSocketManagerFactory factory = new SslSocketManagerFactory();
> AbstractSocketManager manager = factory.createManager("TLS:" + host +
> ':' + port, new SslFactoryData(sslConfig, host, port,
> reconnectionDelayMillis, timeoutMillis, immediateFail, layout));
>
> return new FailFastSyslogAppender(name, layout, filter,
> ignoreExceptions, immediateFlush, manager,
> advertise ? config.getAdvertiser() : null);
> }
>
>
> private static SSLSocketFactory createSslSocketFactory(final
> SslConfiguration sslConf) {
> SSLSocketFactory socketFactory;
> if (sslConf != null) {
> socketFactory = sslConf.getSslSocketFactory();
> } else {
> socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
> }
> return socketFactory;
> }
> private static class SslSocketManagerFactory implements
> ManagerFactory<SslSocketManager, SslFactoryData> {
> private class TlsSocketManagerFactoryException extends Exception {
> private static final long serialVersionUID = 1L;
> }
> @Override
> public SslSocketManager createManager(final String name, final
> SslFactoryData data) {
> InetAddress inetAddress = null;
> OutputStream os = null;
> Socket socket = null;
> try {
> inetAddress = resolveAddress(data.host);
> socket = createSocket(data);
> os = socket.getOutputStream();
> checkDelay(data.delayMillis, os);
> }
> catch (final IOException e) {
> LOGGER.error("SslSocketManager ({})", name, e);
> os = new ByteArrayOutputStream();
> }
> catch (final TlsSocketManagerFactoryException e) {
> LOGGER.catching(Level.DEBUG, e);
> return null;
> }
> return createManager(name, os, socket, data.sslConfig, inetAddress,
> data.host, data.port, data.delayMillis, data.immediateFail, data.layout);
> }
> private InetAddress resolveAddress(final String hostName) throws
> TlsSocketManagerFactoryException {
> InetAddress address;
> try {
> address = InetAddress.getByName(hostName);
> } catch (final UnknownHostException ex) {
> LOGGER.error("Could not find address of {}", hostName, ex);
> throw new TlsSocketManagerFactoryException();
> }
> return address;
> }
> private void checkDelay(final int delay, final OutputStream os) throws
> TlsSocketManagerFactoryException {
> if (delay == 0 && os == null) {
> throw new TlsSocketManagerFactoryException();
> }
> }
> private Socket createSocket(final SslFactoryData data) throws IOException
> {
> SSLSocketFactory socketFactory;
> SSLSocket socket;
> socketFactory = createSslSocketFactory(data.sslConfig);
> //
> **********************************************************************************
> // This is the changed part
> socket = (SSLSocket) socketFactory.createSocket();
> socket.connect(new InetSocketAddress(data.host, data.port),
> data.timeoutMillis);
> //
> **********************************************************************************
> return socket;
> }
> private SslSocketManager createManager(final String name, final
> OutputStream os, final Socket socket,
> final SslConfiguration sslConfig, final InetAddress inetAddress,
> final String host, final int port,
> final int delay, final boolean immediateFail, final Layout<?
> extends Serializable> layout) {
> return new SslSocketManager(name, os, socket, sslConfig, inetAddress,
> host, port, delay, immediateFail,
> layout);
> }
> }
>
> private static class SslFactoryData {
> protected SslConfiguration sslConfig;
> private final String host;
> private final int port;
> private final int delayMillis;
> private final int timeoutMillis;
> private final boolean immediateFail;
> private final Layout<? extends Serializable> layout;
> public SslFactoryData(final SslConfiguration sslConfig, final String
> host, final int port, final int delayMillis,
> final int timeoutMillis, final boolean immediateFail,
> final Layout<? extends Serializable> layout) {
> this.host = host;
> this.port = port;
> this.delayMillis = delayMillis;
> this.timeoutMillis = timeoutMillis;
> this.immediateFail = immediateFail;
> this.layout = layout;
> this.sslConfig = sslConfig;
> }
> }
> }
> {code}
> Thanks - Sam
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]