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  
23  import org.apache.logging.log4j.core.Filter;
24  import org.apache.logging.log4j.core.Layout;
25  import org.apache.logging.log4j.core.config.Configuration;
26  import org.apache.logging.log4j.core.config.plugins.Plugin;
27  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
28  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
29  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
30  import org.apache.logging.log4j.core.config.plugins.PluginElement;
31  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
32  import org.apache.logging.log4j.core.layout.SerializedLayout;
33  import org.apache.logging.log4j.core.net.AbstractSocketManager;
34  import org.apache.logging.log4j.core.net.Advertiser;
35  import org.apache.logging.log4j.core.net.DatagramSocketManager;
36  import org.apache.logging.log4j.core.net.Protocol;
37  import org.apache.logging.log4j.core.net.SslSocketManager;
38  import org.apache.logging.log4j.core.net.TcpSocketManager;
39  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
40  import org.apache.logging.log4j.core.util.Booleans;
41  
42  /**
43   * An Appender that delivers events over socket connections. Supports both TCP and UDP.
44   */
45  @Plugin(name = "Socket", category = "Core", elementType = "appender", printObject = true)
46  public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketManager> {
47  
48      private static final long serialVersionUID = 1L;
49  
50      private Object advertisement;
51      private final Advertiser advertiser;
52  
53      protected SocketAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
54              final AbstractSocketManager manager, final boolean ignoreExceptions, final boolean immediateFlush,
55              final Advertiser advertiser) {
56          super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
57          if (advertiser != null) {
58              final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
59              configuration.putAll(manager.getContentFormat());
60              configuration.put("contentType", layout.getContentType());
61              configuration.put("name", name);
62              this.advertisement = advertiser.advertise(configuration);
63          }
64          this.advertiser = advertiser;
65      }
66  
67      @Override
68      public void stop() {
69          super.stop();
70          if (this.advertiser != null) {
71              this.advertiser.unadvertise(this.advertisement);
72          }
73      }
74  
75      /**
76       * Creates a socket appender.
77       * 
78       * @param host
79       *            The name of the host to connect to.
80       * @param port
81       *            The port to connect to on the target host.
82       * @param protocol
83       *            The Protocol to use.
84       * @param sslConfig
85       *            The SSL configuration file for TCP/SSL, ignored for UPD.
86       * @param connectTimeoutMillis
87       *            the connect timeout in milliseconds.
88       * @param reconnectDelayMillis
89       *            The interval in which failed writes should be retried.
90       * @param immediateFail
91       *            True if the write should fail if no socket is immediately available.
92       * @param name
93       *            The name of the Appender.
94       * @param immediateFlush
95       *            "true" if data should be flushed on each write.
96       * @param ignoreExceptions
97       *            If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
98       *            are propagated to the caller.
99       * @param layout
100      *            The layout to use (defaults to SerializedLayout).
101      * @param filter
102      *            The Filter or null.
103      * @param advertise
104      *            "true" if the appender configuration should be advertised, "false" otherwise.
105      * @param config
106      *            The Configuration
107      * @return A SocketAppender.
108      */
109     @PluginFactory
110     public static SocketAppender createAppender(
111             // @formatter:off
112             @PluginAttribute("host") final String host,
113             @PluginAttribute(value = "port", defaultInt = 0) final int port,
114             @PluginAttribute("protocol") final Protocol protocol,
115             @PluginElement("SSL") final SslConfiguration sslConfig,
116             @PluginAttribute(value = "connectTimeoutMillis", defaultInt = 0) final int connectTimeoutMillis,
117             @PluginAliases("reconnectionDelay") // deprecated
118             @PluginAttribute(value = "reconnectionDelayMillis", defaultInt = 0) final int reconnectDelayMillis,
119             @PluginAttribute(value = "immediateFail", defaultBoolean = true) final boolean immediateFail,
120             @PluginAttribute("name") final String name,
121             @PluginAttribute(value = "immediateFlush", defaultBoolean = true) boolean immediateFlush,
122             @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final boolean ignoreExceptions,
123             @PluginElement("Layout") Layout<? extends Serializable> layout,
124             @PluginElement("Filter") final Filter filter,
125             @PluginAttribute(value = "advertise", defaultBoolean = false) final boolean advertise, 
126             @PluginConfiguration final Configuration config) {
127             // @formatter:on
128         
129         if (layout == null) {
130             layout = SerializedLayout.createLayout();
131         }
132 
133         if (name == null) {
134             LOGGER.error("No name provided for SocketAppender");
135             return null;
136         }
137 
138         final Protocol actualProtocol = protocol != null ? protocol : Protocol.TCP;
139         if (actualProtocol == Protocol.UDP) {
140             immediateFlush = true;
141         }
142 
143         final AbstractSocketManager manager = createSocketManager(name, actualProtocol, host, port, connectTimeoutMillis,
144                 sslConfig, reconnectDelayMillis, immediateFail, layout);
145 
146         return new SocketAppender(name, layout, filter, manager, ignoreExceptions, immediateFlush,
147                 advertise ? config.getAdvertiser() : null);
148     }
149 
150     /**
151      * Creates a socket appender.
152      * 
153      * @param host
154      *            The name of the host to connect to.
155      * @param portNum
156      *            The port to connect to on the target host.
157      * @param protocolIn
158      *            The Protocol to use.
159      * @param sslConfig
160      *            The SSL configuration file for TCP/SSL, ignored for UPD.
161      * @param connectTimeoutMillis
162      *            the connect timeout in milliseconds.
163      * @param delayMillis
164      *            The interval in which failed writes should be retried.
165      * @param immediateFail
166      *            True if the write should fail if no socket is immediately available.
167      * @param name
168      *            The name of the Appender.
169      * @param immediateFlush
170      *            "true" if data should be flushed on each write.
171      * @param ignore
172      *            If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
173      *            are propagated to the caller.
174      * @param layout
175      *            The layout to use (defaults to SerializedLayout).
176      * @param filter
177      *            The Filter or null.
178      * @param advertise
179      *            "true" if the appender configuration should be advertised, "false" otherwise.
180      * @param config
181      *            The Configuration
182      * @return A SocketAppender.
183      * @deprecated Use {@link #createAppender(String, String, Protocol, SslConfiguration, int, String, String, String, String, String, Layout, Filter, String, Configuration)}.
184      */
185     @Deprecated
186     public static SocketAppender createAppender(
187             // @formatter:off
188             final String host,
189             final String portNum,
190             final String protocolIn,
191             final SslConfiguration sslConfig,
192             final int connectTimeoutMillis,
193             // deprecated
194             final String delayMillis,
195             final String immediateFail,
196             final String name,
197             final String immediateFlush,
198             final String ignore,
199             Layout<? extends Serializable> layout,
200             final Filter filter,
201             final String advertise, 
202             final Configuration config) {
203             // @formatter:on
204         boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
205         final boolean isAdvertise = Boolean.parseBoolean(advertise);
206         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
207         final boolean fail = Booleans.parseBoolean(immediateFail, true);
208         final int reconnectDelayMillis = AbstractAppender.parseInt(delayMillis, 0);
209         final int port = AbstractAppender.parseInt(portNum, 0);
210         final Protocol p = protocolIn == null ? Protocol.UDP : Protocol.valueOf(protocolIn);
211         return createAppender(host, port, p, sslConfig, connectTimeoutMillis, reconnectDelayMillis, fail, name, isFlush,
212                 ignoreExceptions, layout, filter, isAdvertise, config);
213     }
214 
215     /**
216      * Creates an AbstractSocketManager for TCP, UDP, and SSL.
217      * 
218      * @throws IllegalArgumentException
219      *             if the protocol cannot be handled.
220      */
221     protected static AbstractSocketManager createSocketManager(final String name, Protocol protocol, final String host,
222             final int port, final int connectTimeoutMillis, final SslConfiguration sslConfig, final int delayMillis,
223             final boolean immediateFail, final Layout<? extends Serializable> layout) {
224         if (protocol == Protocol.TCP && sslConfig != null) {
225             // Upgrade TCP to SSL if an SSL config is specified.
226             protocol = Protocol.SSL;
227         }
228         if (protocol != Protocol.SSL && sslConfig != null) {
229             LOGGER.info("Appender {} ignoring SSL configuration for {} protocol", name, protocol);
230         }
231         switch (protocol) {
232         case TCP:
233             return TcpSocketManager.getSocketManager(host, port, connectTimeoutMillis, delayMillis, immediateFail,
234                     layout);
235         case UDP:
236             return DatagramSocketManager.getSocketManager(host, port, layout);
237         case SSL:
238             return SslSocketManager.getSocketManager(sslConfig, host, port, connectTimeoutMillis, delayMillis,
239                     immediateFail, layout);
240         default:
241             throw new IllegalArgumentException(protocol.toString());
242         }
243     }
244 }