001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender;
018
019import java.io.Serializable;
020import java.util.HashMap;
021import java.util.Map;
022
023import org.apache.logging.log4j.core.Filter;
024import org.apache.logging.log4j.core.Layout;
025import org.apache.logging.log4j.core.config.Configuration;
026import org.apache.logging.log4j.core.config.plugins.Plugin;
027import org.apache.logging.log4j.core.config.plugins.PluginAliases;
028import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
029import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
030import org.apache.logging.log4j.core.config.plugins.PluginElement;
031import org.apache.logging.log4j.core.config.plugins.PluginFactory;
032import org.apache.logging.log4j.core.layout.SerializedLayout;
033import org.apache.logging.log4j.core.net.AbstractSocketManager;
034import org.apache.logging.log4j.core.net.Advertiser;
035import org.apache.logging.log4j.core.net.DatagramSocketManager;
036import org.apache.logging.log4j.core.net.Protocol;
037import org.apache.logging.log4j.core.net.SslSocketManager;
038import org.apache.logging.log4j.core.net.TcpSocketManager;
039import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
040import org.apache.logging.log4j.core.util.Booleans;
041import org.apache.logging.log4j.util.EnglishEnums;
042
043/**
044 * An Appender that delivers events over socket connections. Supports both TCP and UDP.
045 */
046@Plugin(name = "Socket", category = "Core", elementType = "appender", printObject = true)
047public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> {
048
049    private static final long serialVersionUID = 1L;
050
051    private Object advertisement;
052    private final Advertiser advertiser;
053
054    protected SocketAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
055            final AbstractSocketManager manager, final boolean ignoreExceptions, final boolean immediateFlush,
056            final Advertiser advertiser) {
057        super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
058        if (advertiser != null) {
059            final Map<String, String> configuration = new HashMap<String, String>(layout.getContentFormat());
060            configuration.putAll(manager.getContentFormat());
061            configuration.put("contentType", layout.getContentType());
062            configuration.put("name", name);
063            this.advertisement = advertiser.advertise(configuration);
064        }
065        this.advertiser = advertiser;
066    }
067
068    @Override
069    public void stop() {
070        super.stop();
071        if (this.advertiser != null) {
072            this.advertiser.unadvertise(this.advertisement);
073        }
074    }
075
076    /**
077     * 
078     * @param host
079     *        The name of the host to connect to.
080     * @param portNum
081     *        The port to connect to on the target host.
082     * @param protocolStr
083     *        The Protocol to use.
084     * @param sslConfig
085     *        The SSL configuration file for TCP/SSL, ignored for UPD.
086     * @param connectTimeoutMillis
087     *        the connect timeout in milliseconds.
088     * @param delayMillis
089     *        The interval in which failed writes should be retried.
090     * @param immediateFail
091     *        True if the write should fail if no socket is immediately available.
092     * @param name
093     *        The name of the Appender.
094     * @param immediateFlush
095     *        "true" if data should be flushed on each write.
096     * @param ignore
097     *        If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they are
098     *        propagated to the caller.
099     * @param layout
100     *        The layout to use (defaults to SerializedLayout).
101     * @param filter
102     *        The Filter or null.
103     * @param advertise
104     *        "true" if the appender configuration should be advertised, "false" otherwise.
105     * @param config
106     *        The Configuration
107     * @return A SocketAppender.
108     */
109    @PluginFactory
110    public static SocketAppender createAppender(
111            // @formatter:off
112            @PluginAttribute("host") final String host,
113            @PluginAttribute("port") final String portNum,
114            @PluginAttribute("protocol") final String protocolStr,
115            @PluginElement("SSL") final SslConfiguration sslConfig,
116            @PluginAttribute(value = "connectTimeoutMillis", defaultInt = 0) final int connectTimeoutMillis,
117            @PluginAliases("reconnectionDelay") // deprecated
118            @PluginAttribute("reconnectionDelayMillis") final String delayMillis,
119            @PluginAttribute("immediateFail") final String immediateFail,
120            @PluginAttribute("name") final String name,
121            @PluginAttribute("immediateFlush") final String immediateFlush,
122            @PluginAttribute("ignoreExceptions") final String ignore,
123            @PluginElement("Layout") Layout<? extends Serializable> layout,
124            @PluginElement("Filter") final Filter filter, 
125            @PluginAttribute("advertise") final String advertise, @PluginConfiguration final Configuration config) {
126            // @formatter:on
127        boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
128        final boolean isAdvertise = Boolean.parseBoolean(advertise);
129        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
130        final boolean fail = Booleans.parseBoolean(immediateFail, true);
131        final int reconnectDelayMillis = AbstractAppender.parseInt(delayMillis, 0);
132        final int port = AbstractAppender.parseInt(portNum, 0);
133        if (layout == null) {
134            layout = SerializedLayout.createLayout();
135        }
136
137        if (name == null) {
138            LOGGER.error("No name provided for SocketAppender");
139            return null;
140        }
141
142        final Protocol protocol = EnglishEnums.valueOf(Protocol.class,
143                protocolStr != null ? protocolStr : Protocol.TCP.name());
144        if (protocol == Protocol.UDP) {
145            isFlush = true;
146        }
147
148        final AbstractSocketManager manager = createSocketManager(name, protocol, host, port, connectTimeoutMillis,
149                sslConfig, reconnectDelayMillis, fail, layout);
150
151        return new SocketAppender(name, layout, filter, manager, ignoreExceptions, isFlush,
152                isAdvertise ? config.getAdvertiser() : null);
153    }
154
155    /**
156     * Creates an AbstractSocketManager for TCP, UDP, and SSL.
157     * 
158     * @throws IllegalArgumentException
159     *         if the protocol cannot be handled.
160     */
161    protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host,
162            final int port, int connectTimeoutMillis, final SslConfiguration sslConfig, final int delayMillis,
163            final boolean immediateFail, final Layout<? extends Serializable> layout) {
164        if (protocol == Protocol.TCP && sslConfig != null) {
165            // Upgrade TCP to SSL if an SSL config is specified.
166            protocol = Protocol.SSL;
167        }
168        if (protocol != Protocol.SSL && sslConfig != null) {
169            LOGGER.info("Appender {} ignoring SSL configuration for {} protocol", name, protocol);
170        }
171        switch (protocol) {
172        case TCP:
173            return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, delayMillis, immediateFail,
174                    layout);
175        case UDP:
176            return DatagramSocketManager.getSocketManager(host, port, layout);
177        case SSL:
178            return SslSocketManager.getSocketManager(sslConfig, host, port, connectTimeoutMillis, delayMillis,
179                    immediateFail, layout);
180        default:
181            throw new IllegalArgumentException(protocol.toString());
182        }
183    }
184}