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; 022import java.util.concurrent.TimeUnit; 023 024import org.apache.logging.log4j.core.AbstractLifeCycle; 025import org.apache.logging.log4j.core.Appender; 026import org.apache.logging.log4j.core.Core; 027import org.apache.logging.log4j.core.Filter; 028import org.apache.logging.log4j.core.Layout; 029import org.apache.logging.log4j.core.LogEvent; 030import org.apache.logging.log4j.core.config.Configuration; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginAliases; 033import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 034import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 035import org.apache.logging.log4j.core.config.plugins.PluginElement; 036import org.apache.logging.log4j.core.config.plugins.PluginFactory; 037import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost; 038import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort; 039import org.apache.logging.log4j.core.net.AbstractSocketManager; 040import org.apache.logging.log4j.core.net.Advertiser; 041import org.apache.logging.log4j.core.net.DatagramSocketManager; 042import org.apache.logging.log4j.core.net.Protocol; 043import org.apache.logging.log4j.core.net.SocketOptions; 044import org.apache.logging.log4j.core.net.SslSocketManager; 045import org.apache.logging.log4j.core.net.TcpSocketManager; 046import org.apache.logging.log4j.core.net.ssl.SslConfiguration; 047import org.apache.logging.log4j.core.util.Booleans; 048 049/** 050 * An Appender that delivers events over socket connections. Supports both TCP and UDP. 051 */ 052@Plugin(name = "Socket", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 053public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> { 054 055 /** 056 * Subclasses can extend this abstract Builder. 057 * <h1>Defaults</h1> 058 * <ul> 059 * <li>host: "localhost"</li> 060 * <li>protocol: "TCP"</li> 061 * </ul> 062 * <h1>Changes</h1> 063 * <ul> 064 * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li> 065 * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li> 066 * </ul> 067 * 068 * @param <B> 069 * The type to build. 070 */ 071 public static abstract class AbstractBuilder<B extends AbstractBuilder<B>> extends AbstractOutputStreamAppender.Builder<B> { 072 073 @PluginBuilderAttribute 074 private boolean advertise; 075 076 @PluginBuilderAttribute 077 private int connectTimeoutMillis; 078 079 @PluginBuilderAttribute 080 @ValidHost 081 private String host = "localhost"; 082 083 @PluginBuilderAttribute 084 private boolean immediateFail = true; 085 086 @PluginBuilderAttribute 087 @ValidPort 088 private int port; 089 090 @PluginBuilderAttribute 091 private Protocol protocol = Protocol.TCP; 092 093 @PluginBuilderAttribute 094 @PluginAliases({ "reconnectDelay", "reconnectionDelay", "delayMillis", "reconnectionDelayMillis" }) 095 private int reconnectDelayMillis; 096 097 @PluginElement("SocketOptions") 098 private SocketOptions socketOptions; 099 100 @PluginElement("SslConfiguration") 101 @PluginAliases({ "SslConfig" }) 102 private SslConfiguration sslConfiguration; 103 104 public boolean getAdvertise() { 105 return advertise; 106 } 107 108 public int getConnectTimeoutMillis() { 109 return connectTimeoutMillis; 110 } 111 112 public String getHost() { 113 return host; 114 } 115 116 public int getPort() { 117 return port; 118 } 119 120 public Protocol getProtocol() { 121 return protocol; 122 } 123 124 public SslConfiguration getSslConfiguration() { 125 return sslConfiguration; 126 } 127 128 public boolean getImmediateFail() { 129 return immediateFail; 130 } 131 132 public B withAdvertise(final boolean advertise) { 133 this.advertise = advertise; 134 return asBuilder(); 135 } 136 137 public B withConnectTimeoutMillis(final int connectTimeoutMillis) { 138 this.connectTimeoutMillis = connectTimeoutMillis; 139 return asBuilder(); 140 } 141 142 public B withHost(final String host) { 143 this.host = host; 144 return asBuilder(); 145 } 146 147 public B withImmediateFail(final boolean immediateFail) { 148 this.immediateFail = immediateFail; 149 return asBuilder(); 150 } 151 152 public B withPort(final int port) { 153 this.port = port; 154 return asBuilder(); 155 } 156 157 public B withProtocol(final Protocol protocol) { 158 this.protocol = protocol; 159 return asBuilder(); 160 } 161 162 public B withReconnectDelayMillis(final int reconnectDelayMillis) { 163 this.reconnectDelayMillis = reconnectDelayMillis; 164 return asBuilder(); 165 } 166 167 public B withSocketOptions(final SocketOptions socketOptions) { 168 this.socketOptions = socketOptions; 169 return asBuilder(); 170 } 171 172 public B withSslConfiguration(final SslConfiguration sslConfiguration) { 173 this.sslConfiguration = sslConfiguration; 174 return asBuilder(); 175 } 176 177 public int getReconnectDelayMillis() { 178 return reconnectDelayMillis; 179 } 180 181 public SocketOptions getSocketOptions() { 182 return socketOptions; 183 } 184 185 } 186 187 /** 188 * Builds a SocketAppender. 189 * <ul> 190 * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li> 191 * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li> 192 * </ul> 193 */ 194 public static class Builder extends AbstractBuilder<Builder> 195 implements org.apache.logging.log4j.core.util.Builder<SocketAppender> { 196 197 @SuppressWarnings("resource") 198 @Override 199 public SocketAppender build() { 200 boolean immediateFlush = isImmediateFlush(); 201 final boolean bufferedIo = isBufferedIo(); 202 final Layout<? extends Serializable> layout = getLayout(); 203 if (layout == null) { 204 AbstractLifeCycle.LOGGER.error("No layout provided for SocketAppender"); 205 return null; 206 } 207 208 final String name = getName(); 209 if (name == null) { 210 AbstractLifeCycle.LOGGER.error("No name provided for SocketAppender"); 211 return null; 212 } 213 214 final Protocol protocol = getProtocol(); 215 final Protocol actualProtocol = protocol != null ? protocol : Protocol.TCP; 216 if (actualProtocol == Protocol.UDP) { 217 immediateFlush = true; 218 } 219 220 final AbstractSocketManager manager = SocketAppender.createSocketManager(name, actualProtocol, getHost(), getPort(), 221 getConnectTimeoutMillis(), getSslConfiguration(), getReconnectDelayMillis(), getImmediateFail(), layout, getBufferSize(), getSocketOptions()); 222 223 return new SocketAppender(name, layout, getFilter(), manager, isIgnoreExceptions(), 224 !bufferedIo || immediateFlush, getAdvertise() ? getConfiguration().getAdvertiser() : null); 225 } 226 } 227 228 @PluginBuilderFactory 229 public static Builder newBuilder() { 230 return new Builder(); 231 } 232 233 private final Object advertisement; 234 private final Advertiser advertiser; 235 236 protected SocketAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, 237 final AbstractSocketManager manager, final boolean ignoreExceptions, final boolean immediateFlush, 238 final Advertiser advertiser) { 239 super(name, layout, filter, ignoreExceptions, immediateFlush, manager); 240 if (advertiser != null) { 241 final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); 242 configuration.putAll(manager.getContentFormat()); 243 configuration.put("contentType", layout.getContentType()); 244 configuration.put("name", name); 245 this.advertisement = advertiser.advertise(configuration); 246 } else { 247 this.advertisement = null; 248 } 249 this.advertiser = advertiser; 250 } 251 252 @Override 253 public boolean stop(final long timeout, final TimeUnit timeUnit) { 254 setStopping(); 255 super.stop(timeout, timeUnit, false); 256 if (this.advertiser != null) { 257 this.advertiser.unadvertise(this.advertisement); 258 } 259 setStopped(); 260 return true; 261 } 262 263 /** 264 * Creates a socket appender. 265 * 266 * @param host 267 * The name of the host to connect to. 268 * @param port 269 * The port to connect to on the target host. 270 * @param protocol 271 * The Protocol to use. 272 * @param sslConfig 273 * The SSL configuration file for TCP/SSL, ignored for UPD. 274 * @param connectTimeoutMillis 275 * the connect timeout in milliseconds. 276 * @param reconnectDelayMillis 277 * The interval in which failed writes should be retried. 278 * @param immediateFail 279 * True if the write should fail if no socket is immediately available. 280 * @param name 281 * The name of the Appender. 282 * @param immediateFlush 283 * "true" if data should be flushed on each write. 284 * @param ignoreExceptions 285 * If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they 286 * are propagated to the caller. 287 * @param layout 288 * The layout to use. Required, there is no default. 289 * @param filter 290 * The Filter or null. 291 * @param advertise 292 * "true" if the appender configuration should be advertised, "false" otherwise. 293 * @param configuration 294 * The Configuration 295 * @return A SocketAppender. 296 * @deprecated Deprecated in 2.7; use {@link #newBuilder()} 297 */ 298 @Deprecated 299 @PluginFactory 300 public static SocketAppender createAppender( 301 // @formatter:off 302 final String host, 303 final int port, 304 final Protocol protocol, 305 final SslConfiguration sslConfig, 306 final int connectTimeoutMillis, 307 final int reconnectDelayMillis, 308 final boolean immediateFail, 309 final String name, 310 final boolean immediateFlush, 311 final boolean ignoreExceptions, 312 final Layout<? extends Serializable> layout, 313 final Filter filter, 314 final boolean advertise, 315 final Configuration configuration) { 316 // @formatter:on 317 318 // @formatter:off 319 return newBuilder() 320 .withAdvertise(advertise) 321 .setConfiguration(configuration) 322 .withConnectTimeoutMillis(connectTimeoutMillis) 323 .withFilter(filter) 324 .withHost(host) 325 .withIgnoreExceptions(ignoreExceptions) 326 .withImmediateFail(immediateFail) 327 .withLayout(layout) 328 .withName(name) 329 .withPort(port) 330 .withProtocol(protocol) 331 .withReconnectDelayMillis(reconnectDelayMillis) 332 .withSslConfiguration(sslConfig) 333 .build(); 334 // @formatter:on 335 } 336 337 /** 338 * Creates a socket appender. 339 * 340 * @param host 341 * The name of the host to connect to. 342 * @param portNum 343 * The port to connect to on the target host. 344 * @param protocolIn 345 * The Protocol to use. 346 * @param sslConfig 347 * The SSL configuration file for TCP/SSL, ignored for UPD. 348 * @param connectTimeoutMillis 349 * the connect timeout in milliseconds. 350 * @param delayMillis 351 * The interval in which failed writes should be retried. 352 * @param immediateFail 353 * True if the write should fail if no socket is immediately available. 354 * @param name 355 * The name of the Appender. 356 * @param immediateFlush 357 * "true" if data should be flushed on each write. 358 * @param ignore 359 * If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they 360 * are propagated to the caller. 361 * @param layout 362 * The layout to use. Required, there is no default. 363 * @param filter 364 * The Filter or null. 365 * @param advertise 366 * "true" if the appender configuration should be advertised, "false" otherwise. 367 * @param config 368 * The Configuration 369 * @return A SocketAppender. 370 * @deprecated Deprecated in 2.5; use {@link #newBuilder()} 371 */ 372 @Deprecated 373 public static SocketAppender createAppender( 374 // @formatter:off 375 final String host, 376 final String portNum, 377 final String protocolIn, 378 final SslConfiguration sslConfig, 379 final int connectTimeoutMillis, 380 // deprecated 381 final String delayMillis, 382 final String immediateFail, 383 final String name, 384 final String immediateFlush, 385 final String ignore, 386 final Layout<? extends Serializable> layout, 387 final Filter filter, 388 final String advertise, 389 final Configuration config) { 390 // @formatter:on 391 final boolean isFlush = Booleans.parseBoolean(immediateFlush, true); 392 final boolean isAdvertise = Boolean.parseBoolean(advertise); 393 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true); 394 final boolean fail = Booleans.parseBoolean(immediateFail, true); 395 final int reconnectDelayMillis = AbstractAppender.parseInt(delayMillis, 0); 396 final int port = AbstractAppender.parseInt(portNum, 0); 397 final Protocol p = protocolIn == null ? Protocol.UDP : Protocol.valueOf(protocolIn); 398 return createAppender(host, port, p, sslConfig, connectTimeoutMillis, reconnectDelayMillis, fail, name, isFlush, 399 ignoreExceptions, layout, filter, isAdvertise, config); 400 } 401 402 /** 403 * Creates an AbstractSocketManager for TCP, UDP, and SSL. 404 * 405 * @throws IllegalArgumentException 406 * if the protocol cannot be handled. 407 * @deprecated Use {@link #createSocketManager(String, Protocol, String, int, int, SslConfiguration, int, boolean, Layout, int, SocketOptions)}. 408 */ 409 @Deprecated 410 protected static AbstractSocketManager createSocketManager(final String name, final Protocol protocol, final String host, 411 final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, final int reconnectDelayMillis, 412 final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) { 413 return createSocketManager(name, protocol, host, port, connectTimeoutMillis, sslConfig, reconnectDelayMillis, immediateFail, layout, bufferSize, null); 414 } 415 416 /** 417 * Creates an AbstractSocketManager for TCP, UDP, and SSL. 418 * 419 * @throws IllegalArgumentException 420 * if the protocol cannot be handled. 421 */ 422 protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host, 423 final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, 424 final int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout, 425 final int bufferSize, final SocketOptions socketOptions) { 426 if (protocol == Protocol.TCP && sslConfig != null) { 427 // Upgrade TCP to SSL if an SSL config is specified. 428 protocol = Protocol.SSL; 429 } 430 if (protocol != Protocol.SSL && sslConfig != null) { 431 LOGGER.info("Appender {} ignoring SSL configuration for {} protocol", name, protocol); 432 } 433 switch (protocol) { 434 case TCP: 435 return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, reconnectDelayMillis, 436 immediateFail, layout, bufferSize, socketOptions); 437 case UDP: 438 return DatagramSocketManager.getSocketManager(host, port, layout, bufferSize); 439 case SSL: 440 return SslSocketManager.getSocketManager(sslConfig, host, port, connectTimeoutMillis, reconnectDelayMillis, 441 immediateFail, layout, bufferSize, socketOptions); 442 default: 443 throw new IllegalArgumentException(protocol.toString()); 444 } 445 } 446 447 @Override 448 protected void directEncodeEvent(final LogEvent event) { 449 // Disable garbage-free logging for now: 450 // problem with UDP: 8K buffer size means that largish messages get broken up into chunks 451 writeByteArrayToManager(event); // revert to classic (non-garbage free) logging 452 } 453}