blob: 8e9de102f9668e5af0146d6ff42b5155b1e42911 [file] [log] [blame]
/*
* 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 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.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.plugins.Plugin;
import org.apache.logging.log4j.plugins.PluginAliases;
import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.plugins.PluginElement;
import org.apache.logging.log4j.plugins.PluginFactory;
import org.apache.logging.log4j.plugins.validation.constraints.ValidHost;
import org.apache.logging.log4j.plugins.validation.constraints.ValidPort;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 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 setAdvertise(final boolean advertise) {
this.advertise = advertise;
return asBuilder();
}
public B setConnectTimeoutMillis(final int connectTimeoutMillis) {
this.connectTimeoutMillis = connectTimeoutMillis;
return asBuilder();
}
public B setHost(final String host) {
this.host = host;
return asBuilder();
}
public B setImmediateFail(final boolean immediateFail) {
this.immediateFail = immediateFail;
return asBuilder();
}
public B setPort(final int port) {
this.port = port;
return asBuilder();
}
public B setProtocol(final Protocol protocol) {
this.protocol = protocol;
return asBuilder();
}
public B setReconnectDelayMillis(final int reconnectDelayMillis) {
this.reconnectDelayMillis = reconnectDelayMillis;
return asBuilder();
}
public B setSocketOptions(final SocketOptions socketOptions) {
this.socketOptions = socketOptions;
return asBuilder();
}
public B setSslConfiguration(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.plugins.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);
}
}
@PluginFactory
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, null, 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 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
}
}