| /* |
| * 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.Serializable; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.logging.log4j.core.AbstractLifeCycle; |
| 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.LogEvent; |
| 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.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.config.plugins.PluginFactory; |
| import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost; |
| import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort; |
| 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; |
| |
| /** |
| * An Appender that delivers events over socket connections. Supports both TCP and UDP. |
| */ |
| @Plugin(name = "Socket", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) |
| public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> { |
| |
| /** |
| * Subclasses can extend this abstract Builder. |
| * <h1>Defaults</h1> |
| * <ul> |
| * <li>host: "localhost"</li> |
| * <li>protocol: "TCP"</li> |
| * </ul> |
| * <h1>Changes</h1> |
| * <ul> |
| * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li> |
| * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li> |
| * </ul> |
| * |
| * @param <B> |
| * The type to build. |
| */ |
| public static abstract class AbstractBuilder<B extends AbstractBuilder<B>> extends AbstractOutputStreamAppender.Builder<B> { |
| |
| @PluginBuilderAttribute |
| private boolean advertise; |
| |
| @PluginBuilderAttribute |
| private int connectTimeoutMillis; |
| |
| @PluginBuilderAttribute |
| @ValidHost |
| private String host = "localhost"; |
| |
| @PluginBuilderAttribute |
| private boolean immediateFail = true; |
| |
| @PluginBuilderAttribute |
| @ValidPort |
| private int port; |
| |
| @PluginBuilderAttribute |
| private Protocol protocol = Protocol.TCP; |
| |
| @PluginBuilderAttribute |
| @PluginAliases({ "reconnectDelay", "reconnectionDelay", "delayMillis", "reconnectionDelayMillis" }) |
| private int reconnectDelayMillis; |
| |
| @PluginElement("SocketOptions") |
| private SocketOptions socketOptions; |
| |
| @PluginElement("SslConfiguration") |
| @PluginAliases({ "SslConfig" }) |
| private SslConfiguration sslConfiguration; |
| |
| public boolean getAdvertise() { |
| return advertise; |
| } |
| |
| public int getConnectTimeoutMillis() { |
| return connectTimeoutMillis; |
| } |
| |
| public String getHost() { |
| return host; |
| } |
| |
| public int getPort() { |
| return port; |
| } |
| |
| public Protocol getProtocol() { |
| return protocol; |
| } |
| |
| public SslConfiguration getSslConfiguration() { |
| return sslConfiguration; |
| } |
| |
| public boolean getImmediateFail() { |
| return immediateFail; |
| } |
| |
| public B withAdvertise(final boolean advertise) { |
| this.advertise = advertise; |
| return asBuilder(); |
| } |
| |
| public B withConnectTimeoutMillis(final int connectTimeoutMillis) { |
| this.connectTimeoutMillis = connectTimeoutMillis; |
| return asBuilder(); |
| } |
| |
| public B withHost(final String host) { |
| this.host = host; |
| return asBuilder(); |
| } |
| |
| public B withImmediateFail(final boolean immediateFail) { |
| this.immediateFail = immediateFail; |
| return asBuilder(); |
| } |
| |
| public B withPort(final int port) { |
| this.port = port; |
| return asBuilder(); |
| } |
| |
| public B withProtocol(final Protocol protocol) { |
| this.protocol = protocol; |
| return asBuilder(); |
| } |
| |
| public B withReconnectDelayMillis(final int reconnectDelayMillis) { |
| this.reconnectDelayMillis = reconnectDelayMillis; |
| return asBuilder(); |
| } |
| |
| public B withSocketOptions(final SocketOptions socketOptions) { |
| this.socketOptions = socketOptions; |
| return asBuilder(); |
| } |
| |
| public B withSslConfiguration(final SslConfiguration sslConfiguration) { |
| this.sslConfiguration = sslConfiguration; |
| return asBuilder(); |
| } |
| |
| public int getReconnectDelayMillis() { |
| return reconnectDelayMillis; |
| } |
| |
| public SocketOptions getSocketOptions() { |
| return socketOptions; |
| } |
| |
| } |
| |
| /** |
| * Builds a SocketAppender. |
| * <ul> |
| * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li> |
| * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li> |
| * </ul> |
| */ |
| public static class Builder extends AbstractBuilder<Builder> |
| implements org.apache.logging.log4j.core.util.Builder<SocketAppender> { |
| |
| @SuppressWarnings("resource") |
| @Override |
| public SocketAppender build() { |
| boolean immediateFlush = isImmediateFlush(); |
| final boolean bufferedIo = isBufferedIo(); |
| final Layout<? extends Serializable> layout = getLayout(); |
| if (layout == null) { |
| AbstractLifeCycle.LOGGER.error("No layout provided for SocketAppender"); |
| return null; |
| } |
| |
| final String name = getName(); |
| if (name == null) { |
| AbstractLifeCycle.LOGGER.error("No name provided for SocketAppender"); |
| return null; |
| } |
| |
| final Protocol protocol = getProtocol(); |
| final Protocol actualProtocol = protocol != null ? protocol : Protocol.TCP; |
| if (actualProtocol == Protocol.UDP) { |
| immediateFlush = true; |
| } |
| |
| final AbstractSocketManager manager = SocketAppender.createSocketManager(name, actualProtocol, getHost(), getPort(), |
| getConnectTimeoutMillis(), getSslConfiguration(), getReconnectDelayMillis(), getImmediateFail(), layout, getBufferSize(), getSocketOptions()); |
| |
| return new SocketAppender(name, layout, getFilter(), manager, isIgnoreExceptions(), |
| !bufferedIo || immediateFlush, getAdvertise() ? getConfiguration().getAdvertiser() : null); |
| } |
| } |
| |
| @PluginBuilderFactory |
| public static Builder newBuilder() { |
| return new Builder(); |
| } |
| |
| private final Object advertisement; |
| private final Advertiser advertiser; |
| |
| protected SocketAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, |
| final AbstractSocketManager manager, final boolean ignoreExceptions, final boolean immediateFlush, |
| final Advertiser advertiser) { |
| super(name, layout, filter, ignoreExceptions, immediateFlush, manager); |
| if (advertiser != null) { |
| final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); |
| configuration.putAll(manager.getContentFormat()); |
| configuration.put("contentType", layout.getContentType()); |
| configuration.put("name", name); |
| this.advertisement = advertiser.advertise(configuration); |
| } else { |
| this.advertisement = null; |
| } |
| this.advertiser = advertiser; |
| } |
| |
| @Override |
| public boolean stop(final long timeout, final TimeUnit timeUnit) { |
| setStopping(); |
| super.stop(timeout, timeUnit, false); |
| if (this.advertiser != null) { |
| this.advertiser.unadvertise(this.advertisement); |
| } |
| setStopped(); |
| return true; |
| } |
| |
| /** |
| * Creates a socket appender. |
| * |
| * @param host |
| * The name of the host to connect to. |
| * @param port |
| * The port to connect to on the target host. |
| * @param protocol |
| * The Protocol to use. |
| * @param sslConfig |
| * The SSL configuration file for TCP/SSL, ignored for UPD. |
| * @param connectTimeoutMillis |
| * the connect timeout in milliseconds. |
| * @param reconnectDelayMillis |
| * 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 layout |
| * The layout to use. Required, there is no default. |
| * @param filter |
| * The Filter or null. |
| * @param advertise |
| * "true" if the appender configuration should be advertised, "false" otherwise. |
| * @param configuration |
| * The Configuration |
| * @return A SocketAppender. |
| * @deprecated Deprecated in 2.7; use {@link #newBuilder()} |
| */ |
| @Deprecated |
| @PluginFactory |
| public static SocketAppender createAppender( |
| // @formatter:off |
| final String host, |
| final int port, |
| final Protocol protocol, |
| final SslConfiguration sslConfig, |
| final int connectTimeoutMillis, |
| final int reconnectDelayMillis, |
| final boolean immediateFail, |
| final String name, |
| final boolean immediateFlush, |
| final boolean ignoreExceptions, |
| final Layout<? extends Serializable> layout, |
| final Filter filter, |
| final boolean advertise, |
| final Configuration configuration) { |
| // @formatter:on |
| |
| // @formatter:off |
| return newBuilder() |
| .withAdvertise(advertise) |
| .setConfiguration(configuration) |
| .withConnectTimeoutMillis(connectTimeoutMillis) |
| .withFilter(filter) |
| .withHost(host) |
| .withIgnoreExceptions(ignoreExceptions) |
| .withImmediateFail(immediateFail) |
| .withLayout(layout) |
| .withName(name) |
| .withPort(port) |
| .withProtocol(protocol) |
| .withReconnectDelayMillis(reconnectDelayMillis) |
| .withSslConfiguration(sslConfig) |
| .build(); |
| // @formatter:on |
| } |
| |
| /** |
| * Creates a socket appender. |
| * |
| * @param host |
| * The name of the host to connect to. |
| * @param portNum |
| * The port to connect to on the target host. |
| * @param protocolIn |
| * The Protocol to use. |
| * @param sslConfig |
| * The SSL configuration file for TCP/SSL, ignored for UPD. |
| * @param connectTimeoutMillis |
| * the connect timeout in milliseconds. |
| * @param delayMillis |
| * 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 ignore |
| * If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they |
| * are propagated to the caller. |
| * @param layout |
| * The layout to use. Required, there is no default. |
| * @param filter |
| * The Filter or null. |
| * @param advertise |
| * "true" if the appender configuration should be advertised, "false" otherwise. |
| * @param config |
| * The Configuration |
| * @return A SocketAppender. |
| * @deprecated Deprecated in 2.5; use {@link #newBuilder()} |
| */ |
| @Deprecated |
| public static SocketAppender createAppender( |
| // @formatter:off |
| final String host, |
| final String portNum, |
| final String protocolIn, |
| final SslConfiguration sslConfig, |
| final int connectTimeoutMillis, |
| // deprecated |
| final String delayMillis, |
| final String immediateFail, |
| final String name, |
| final String immediateFlush, |
| final String ignore, |
| final Layout<? extends Serializable> layout, |
| final Filter filter, |
| final String advertise, |
| final Configuration config) { |
| // @formatter:on |
| final boolean isFlush = Booleans.parseBoolean(immediateFlush, true); |
| final boolean isAdvertise = Boolean.parseBoolean(advertise); |
| final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true); |
| final boolean fail = Booleans.parseBoolean(immediateFail, true); |
| final int reconnectDelayMillis = AbstractAppender.parseInt(delayMillis, 0); |
| final int port = AbstractAppender.parseInt(portNum, 0); |
| final Protocol p = protocolIn == null ? Protocol.UDP : Protocol.valueOf(protocolIn); |
| return createAppender(host, port, p, sslConfig, connectTimeoutMillis, reconnectDelayMillis, fail, name, isFlush, |
| ignoreExceptions, layout, filter, isAdvertise, config); |
| } |
| |
| /** |
| * Creates an AbstractSocketManager for TCP, UDP, and SSL. |
| * |
| * @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, final 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; |
| } |
| if (protocol != Protocol.SSL && sslConfig != null) { |
| LOGGER.info("Appender {} ignoring SSL configuration for {} protocol", name, protocol); |
| } |
| switch (protocol) { |
| case TCP: |
| 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, socketOptions); |
| default: |
| throw new IllegalArgumentException(protocol.toString()); |
| } |
| } |
| |
| @Override |
| protected void directEncodeEvent(final LogEvent event) { |
| // Disable garbage-free logging for now: |
| // problem with UDP: 8K buffer size means that largish messages get broken up into chunks |
| writeByteArrayToManager(event); // revert to classic (non-garbage free) logging |
| } |
| } |