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