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.net.AbstractSocketManager;
40  import org.apache.logging.log4j.core.net.Advertiser;
41  import org.apache.logging.log4j.core.net.DatagramSocketManager;
42  import org.apache.logging.log4j.core.net.Protocol;
43  import org.apache.logging.log4j.core.net.SocketOptions;
44  import org.apache.logging.log4j.core.net.SslSocketManager;
45  import org.apache.logging.log4j.core.net.TcpSocketManager;
46  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
47  import org.apache.logging.log4j.core.util.Booleans;
48  
49  /**
50   * An Appender that delivers events over socket connections. Supports both TCP and UDP.
51   */
52  @Plugin(name = "Socket", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
53  public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> {
54  
55      /**
56       * Subclasses can extend this abstract Builder.
57       * <h1>Defaults</h1>
58       * <ul>
59       * <li>host: "localhost"</li>
60       * <li>protocol: "TCP"</li>
61       * </ul>
62       * <h1>Changes</h1>
63       * <ul>
64       * <li>Removed deprecated "delayMillis", use "reconnectionDelayMillis".</li>
65       * <li>Removed deprecated "reconnectionDelay", use "reconnectionDelayMillis".</li>
66       * </ul>
67       * 
68       * @param <B>
69       *            The type to build.
70       */
71      public static abstract class AbstractBuilder<B extends AbstractBuilder<B>> extends AbstractOutputStreamAppender.Builder<B> {
72  
73          @PluginBuilderAttribute
74          private boolean advertise;
75  
76          @PluginBuilderAttribute
77          private int connectTimeoutMillis;
78  
79          @PluginBuilderAttribute
80          @ValidHost
81          private String host = "localhost";
82  
83          @PluginBuilderAttribute
84          private boolean immediateFail = true;
85  
86          @PluginBuilderAttribute
87          @ValidPort
88          private int port;
89  
90          @PluginBuilderAttribute
91          private Protocol protocol = Protocol.TCP;
92  
93          @PluginBuilderAttribute
94          @PluginAliases({ "reconnectDelay", "reconnectionDelay", "delayMillis", "reconnectionDelayMillis" })
95          private int reconnectDelayMillis;
96          
97          @PluginElement("SocketOptions")
98          private SocketOptions socketOptions;
99          
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 }