View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender;
18  
19  import java.io.Serializable;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.concurrent.TimeUnit;
23  
24  import org.apache.logging.log4j.core.AbstractLifeCycle;
25  import org.apache.logging.log4j.core.Appender;
26  import org.apache.logging.log4j.core.Core;
27  import org.apache.logging.log4j.core.Filter;
28  import org.apache.logging.log4j.core.Layout;
29  import org.apache.logging.log4j.core.LogEvent;
30  import org.apache.logging.log4j.core.config.Configuration;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
33  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
34  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
35  import org.apache.logging.log4j.core.config.plugins.PluginElement;
36  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
37  import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost;
38  import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
39  import org.apache.logging.log4j.core.layout.SerializedLayout;
40  import org.apache.logging.log4j.core.net.AbstractSocketManager;
41  import org.apache.logging.log4j.core.net.Advertiser;
42  import org.apache.logging.log4j.core.net.DatagramSocketManager;
43  import org.apache.logging.log4j.core.net.Protocol;
44  import org.apache.logging.log4j.core.net.SocketOptions;
45  import org.apache.logging.log4j.core.net.SslSocketManager;
46  import org.apache.logging.log4j.core.net.TcpSocketManager;
47  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
48  import org.apache.logging.log4j.core.util.Booleans;
49  
50  /**
51   * An Appender that delivers events over socket connections. Supports both TCP and UDP.
52   */
53  @Plugin(name = "Socket", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
54  public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> {
55  
56      /**
57       * Subclasses can extend this abstract Builder.
58       * 
59       * <ul> 
60       * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li>
61       * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li>
62       * </ul> 
63       * 
64       * @param <B>
65       *            The type to build.
66       */
67      public static abstract class AbstractBuilder<B extends AbstractBuilder<B>> extends AbstractOutputStreamAppender.Builder<B> {
68  
69          @PluginBuilderAttribute
70          private boolean advertise;
71  
72          @PluginBuilderAttribute
73          private int connectTimeoutMillis;
74  
75          @PluginBuilderAttribute
76          @ValidHost
77          private String host = "localhost";
78  
79          @PluginBuilderAttribute
80          private boolean immediateFail = true;
81  
82          @PluginBuilderAttribute
83          @ValidPort
84          private int port;
85  
86          @PluginBuilderAttribute
87          private Protocol protocol = Protocol.TCP;
88  
89          @PluginBuilderAttribute
90          @PluginAliases({ "reconnectDelay", "reconnectionDelay", "delayMillis", "reconnectionDelayMillis" })
91          private int reconnectDelayMillis;
92          
93          @PluginElement("SocketOptions")
94          private SocketOptions socketOptions;
95          
96          @PluginElement("SslConfiguration")
97          @PluginAliases({ "SslConfig" })
98          private SslConfiguration sslConfiguration;
99  
100         public boolean getAdvertise() {
101             return advertise;
102         }
103 
104         public int getConnectTimeoutMillis() {
105             return connectTimeoutMillis;
106         }
107 
108         public String getHost() {
109             return host;
110         }
111 
112         public int getPort() {
113             return port;
114         }
115 
116         public Protocol getProtocol() {
117             return protocol;
118         }
119 
120         public SslConfiguration getSslConfiguration() {
121             return sslConfiguration;
122         }
123 
124         public boolean getImmediateFail() {
125             return immediateFail;
126         }
127 
128         public B withAdvertise(final boolean advertise) {
129             this.advertise = advertise;
130             return asBuilder();
131         }
132 
133         public B withConnectTimeoutMillis(final int connectTimeoutMillis) {
134             this.connectTimeoutMillis = connectTimeoutMillis;
135             return asBuilder();
136         }
137 
138         public B withHost(final String host) {
139             this.host = host;
140             return asBuilder();
141         }
142 
143         public B withImmediateFail(final boolean immediateFail) {
144             this.immediateFail = immediateFail;
145             return asBuilder();
146         }
147 
148         public B withPort(final int port) {
149             this.port = port;
150             return asBuilder();
151         }
152 
153         public B withProtocol(final Protocol protocol) {
154             this.protocol = protocol;
155             return asBuilder();
156         }
157 
158         public B withReconnectDelayMillis(final int reconnectDelayMillis) {
159             this.reconnectDelayMillis = reconnectDelayMillis;
160             return asBuilder();
161         }
162 
163         public B withSocketOptions(final SocketOptions socketOptions) {
164             this.socketOptions = socketOptions;
165             return asBuilder();
166         }
167 
168         public B withSslConfiguration(final SslConfiguration sslConfiguration) {
169             this.sslConfiguration = sslConfiguration;
170             return asBuilder();
171         }
172 
173         public int getReconnectDelayMillis() {
174             return reconnectDelayMillis;
175         }
176 
177         public SocketOptions getSocketOptions() {
178             return socketOptions;
179         }
180 
181     }
182     
183     /**
184      * Builds a SocketAppender.
185      * <ul> 
186      * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li>
187      * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li>
188      * </ul> 
189      */
190     public static class Builder extends AbstractBuilder<Builder>
191             implements org.apache.logging.log4j.core.util.Builder<SocketAppender> {
192 
193         @SuppressWarnings("resource")
194         @Override
195         public SocketAppender build() {
196             boolean immediateFlush = isImmediateFlush();
197             final boolean bufferedIo = isBufferedIo();
198             Layout<? extends Serializable> layout = getLayout();
199             if (layout == null) {
200                 layout = SerializedLayout.createLayout();
201             }
202 
203             final String name = getName();
204             if (name == null) {
205                 AbstractLifeCycle.LOGGER.error("No name provided for SocketAppender");
206                 return null;
207             }
208 
209             final Protocol protocol = getProtocol();
210             final Protocol actualProtocol = protocol != null ? protocol : Protocol.TCP;
211             if (actualProtocol == Protocol.UDP) {
212                 immediateFlush = true;
213             }
214 
215             final AbstractSocketManager manager = SocketAppender.createSocketManager(name, actualProtocol, getHost(), getPort(),
216                     getConnectTimeoutMillis(), getSslConfiguration(), getReconnectDelayMillis(), getImmediateFail(), layout, getBufferSize(), getSocketOptions());
217 
218             return new SocketAppender(name, layout, getFilter(), manager, isIgnoreExceptions(),
219                     !bufferedIo || immediateFlush, getAdvertise() ? getConfiguration().getAdvertiser() : null);
220         }
221     }
222     
223     @PluginBuilderFactory
224     public static Builder newBuilder() {
225         return new Builder();
226     }
227 
228     private final Object advertisement;
229     private final Advertiser advertiser;
230 
231     protected SocketAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
232             final AbstractSocketManager manager, final boolean ignoreExceptions, final boolean immediateFlush,
233             final Advertiser advertiser) {
234         super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
235         if (advertiser != null) {
236             final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
237             configuration.putAll(manager.getContentFormat());
238             configuration.put("contentType", layout.getContentType());
239             configuration.put("name", name);
240             this.advertisement = advertiser.advertise(configuration);
241         } else {
242             this.advertisement = null;
243         }
244         this.advertiser = advertiser;
245     }
246 
247     @Override
248     public boolean stop(final long timeout, final TimeUnit timeUnit) {
249         setStopping();
250         super.stop(timeout, timeUnit, false);
251         if (this.advertiser != null) {
252             this.advertiser.unadvertise(this.advertisement);
253         }
254         setStopped();
255         return true;
256     }
257 
258     /**
259      * Creates a socket appender.
260      *
261      * @param host
262      *            The name of the host to connect to.
263      * @param port
264      *            The port to connect to on the target host.
265      * @param protocol
266      *            The Protocol to use.
267      * @param sslConfig
268      *            The SSL configuration file for TCP/SSL, ignored for UPD.
269      * @param connectTimeoutMillis
270      *            the connect timeout in milliseconds.
271      * @param reconnectDelayMillis
272      *            The interval in which failed writes should be retried.
273      * @param immediateFail
274      *            True if the write should fail if no socket is immediately available.
275      * @param name
276      *            The name of the Appender.
277      * @param immediateFlush
278      *            "true" if data should be flushed on each write.
279      * @param ignoreExceptions
280      *            If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
281      *            are propagated to the caller.
282      * @param layout
283      *            The layout to use (defaults to SerializedLayout).
284      * @param filter
285      *            The Filter or null.
286      * @param advertise
287      *            "true" if the appender configuration should be advertised, "false" otherwise.
288      * @param configuration
289      *            The Configuration
290      * @return A SocketAppender.
291      * @deprecated Deprecated in 2.7; use {@link #newBuilder()}
292      */
293     @Deprecated
294     @PluginFactory
295     public static SocketAppender createAppender(
296             // @formatter:off
297             final String host,
298             final int port,
299             final Protocol protocol,
300             final SslConfiguration sslConfig,
301             final int connectTimeoutMillis,
302             final int reconnectDelayMillis,
303             final boolean immediateFail,
304             final String name,
305             final boolean immediateFlush,
306             final boolean ignoreExceptions,
307             final Layout<? extends Serializable> layout,
308             final Filter filter,
309             final boolean advertise,
310             final Configuration configuration) {
311             // @formatter:on
312 
313         // @formatter:off
314         return newBuilder()
315             .withAdvertise(advertise)
316             .setConfiguration(configuration)
317             .withConnectTimeoutMillis(connectTimeoutMillis)
318             .withFilter(filter)
319             .withHost(host)
320             .withIgnoreExceptions(ignoreExceptions)
321             .withImmediateFail(immediateFail)
322             .withLayout(layout)
323             .withName(name)
324             .withPort(port)
325             .withProtocol(protocol)
326             .withReconnectDelayMillis(reconnectDelayMillis)
327             .withSslConfiguration(sslConfig)
328             .build();
329         // @formatter:on
330     }
331     
332     /**
333      * Creates a socket appender.
334      *
335      * @param host
336      *            The name of the host to connect to.
337      * @param portNum
338      *            The port to connect to on the target host.
339      * @param protocolIn
340      *            The Protocol to use.
341      * @param sslConfig
342      *            The SSL configuration file for TCP/SSL, ignored for UPD.
343      * @param connectTimeoutMillis
344      *            the connect timeout in milliseconds.
345      * @param delayMillis
346      *            The interval in which failed writes should be retried.
347      * @param immediateFail
348      *            True if the write should fail if no socket is immediately available.
349      * @param name
350      *            The name of the Appender.
351      * @param immediateFlush
352      *            "true" if data should be flushed on each write.
353      * @param ignore
354      *            If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
355      *            are propagated to the caller.
356      * @param layout
357      *            The layout to use (defaults to {@link SerializedLayout}).
358      * @param filter
359      *            The Filter or null.
360      * @param advertise
361      *            "true" if the appender configuration should be advertised, "false" otherwise.
362      * @param config
363      *            The Configuration
364      * @return A SocketAppender.
365      * @deprecated Deprecated in 2.5; use {@link #newBuilder()}
366      */
367     @Deprecated
368     public static SocketAppender createAppender(
369             // @formatter:off
370             final String host,
371             final String portNum,
372             final String protocolIn,
373             final SslConfiguration sslConfig,
374             final int connectTimeoutMillis,
375             // deprecated
376             final String delayMillis,
377             final String immediateFail,
378             final String name,
379             final String immediateFlush,
380             final String ignore,
381             final Layout<? extends Serializable> layout,
382             final Filter filter,
383             final String advertise,
384             final Configuration config) {
385             // @formatter:on
386         final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
387         final boolean isAdvertise = Boolean.parseBoolean(advertise);
388         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
389         final boolean fail = Booleans.parseBoolean(immediateFail, true);
390         final int reconnectDelayMillis = AbstractAppender.parseInt(delayMillis, 0);
391         final int port = AbstractAppender.parseInt(portNum, 0);
392         final Protocol p = protocolIn == null ? Protocol.UDP : Protocol.valueOf(protocolIn);
393         return createAppender(host, port, p, sslConfig, connectTimeoutMillis, reconnectDelayMillis, fail, name, isFlush,
394                 ignoreExceptions, layout, filter, isAdvertise, config);
395     }
396 
397     /**
398      * Creates an AbstractSocketManager for TCP, UDP, and SSL.
399      *
400      * @throws IllegalArgumentException
401      *             if the protocol cannot be handled.
402      * @deprecated Use {@link #createSocketManager(String, Protocol, String, int, int, SslConfiguration, int, boolean, Layout, int, SocketOptions)}.
403      */
404     @Deprecated
405     protected static AbstractSocketManager createSocketManager(final String name, final Protocol protocol, final String host,
406             final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, final int reconnectDelayMillis,
407             final boolean immediateFail, final Layout<? extends Serializable> layout, final int bufferSize) {
408         return createSocketManager(name, protocol, host, port, connectTimeoutMillis, sslConfig, reconnectDelayMillis, immediateFail, layout, bufferSize, null);
409     }
410 
411     /**
412      * Creates an AbstractSocketManager for TCP, UDP, and SSL.
413      *
414      * @throws IllegalArgumentException
415      *             if the protocol cannot be handled.
416      */
417     protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host,
418             final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig,
419             final int reconnectDelayMillis, final boolean immediateFail, final Layout<? extends Serializable> layout,
420             final int bufferSize, final SocketOptions socketOptions) {
421         if (protocol == Protocol.TCP && sslConfig != null) {
422             // Upgrade TCP to SSL if an SSL config is specified.
423             protocol = Protocol.SSL;
424         }
425         if (protocol != Protocol.SSL && sslConfig != null) {
426             LOGGER.info("Appender {} ignoring SSL configuration for {} protocol", name, protocol);
427         }
428         switch (protocol) {
429         case TCP:
430             return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, reconnectDelayMillis,
431                     immediateFail, layout, bufferSize, socketOptions);
432         case UDP:
433             return DatagramSocketManager.getSocketManager(host, port, layout, bufferSize);
434         case SSL:
435             return SslSocketManager.getSocketManager(sslConfig, host, port, connectTimeoutMillis, reconnectDelayMillis,
436                     immediateFail, layout, bufferSize, socketOptions);
437         default:
438             throw new IllegalArgumentException(protocol.toString());
439         }
440     }
441 
442     @Override
443     protected void directEncodeEvent(final LogEvent event) {
444         // Disable garbage-free logging for now:
445         // problem with UDP: 8K buffer size means that largish messages get broken up into chunks
446         writeByteArrayToManager(event); // revert to classic (non-garbage free) logging
447     }
448 }