logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sam Beroz (JIRA)" <j...@apache.org>
Subject [jira] [Created] (LOG4J2-895) Specify the SyslogAppender timeout value as part of the configuration
Date Mon, 17 Nov 2014 17:36:33 GMT
Sam Beroz created LOG4J2-895:
--------------------------------

             Summary: 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
            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:

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;
        }
    }
}

Thanks - Sam




--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Mime
View raw message