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