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<>(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, final 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}