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