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  
18  package org.apache.logging.log4j.core.appender;
19  
20  import org.apache.logging.log4j.core.Appender;
21  import org.apache.logging.log4j.core.Core;
22  import org.apache.logging.log4j.core.Filter;
23  import org.apache.logging.log4j.core.Layout;
24  import org.apache.logging.log4j.core.LogEvent;
25  import org.apache.logging.log4j.core.config.Configuration;
26  import org.apache.logging.log4j.core.config.DefaultConfiguration;
27  import org.apache.logging.log4j.core.config.Property;
28  import org.apache.logging.log4j.core.config.plugins.Plugin;
29  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
30  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
31  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
32  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
33  import org.apache.logging.log4j.core.config.plugins.PluginElement;
34  import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
35  import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
36  import org.apache.logging.log4j.core.filter.ThresholdFilter;
37  import org.apache.logging.log4j.core.layout.HtmlLayout;
38  import org.apache.logging.log4j.core.net.SmtpManager;
39  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
40  import org.apache.logging.log4j.core.util.Booleans;
41  
42  import java.io.Serializable;
43  
44  /**
45   * Send an e-mail when a specific logging event occurs, typically on errors or
46   * fatal errors.
47   *
48   * <p>
49   * The number of logging events delivered in this e-mail depend on the value of
50   * <b>BufferSize</b> option. The <code>SmtpAppender</code> keeps only the last
51   * <code>BufferSize</code> logging events in its cyclic buffer. This keeps
52   * memory requirements at a reasonable level while still delivering useful
53   * application context.
54   *
55   * By default, an email message will formatted as HTML. This can be modified by
56   * setting a layout for the appender.
57   *
58   * By default, an email message will be sent when an ERROR or higher severity
59   * message is appended. This can be modified by setting a filter for the
60   * appender.
61   */
62  @Plugin(name = "SMTP", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
63  public final class SmtpAppender extends AbstractAppender {
64  
65      private static final int DEFAULT_BUFFER_SIZE = 512;
66  
67      /** The SMTP Manager */
68      private final SmtpManager manager;
69  
70      private SmtpAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
71              final SmtpManager manager, final boolean ignoreExceptions, final Property[] properties) {
72          super(name, filter, layout, ignoreExceptions, properties);
73          this.manager = manager;
74      }
75  
76      /**
77       * @since 2.13.2
78       */
79      public static class Builder extends AbstractAppender.Builder<Builder>
80              implements org.apache.logging.log4j.core.util.Builder<SmtpAppender> {
81          @PluginBuilderAttribute
82          private String to;
83  
84          @PluginBuilderAttribute
85          private String cc;
86  
87          @PluginBuilderAttribute
88          private String bcc;
89  
90          @PluginBuilderAttribute
91          private String from;
92  
93          @PluginBuilderAttribute
94          private String replyTo;
95  
96          @PluginBuilderAttribute
97          private String subject;
98  
99          @PluginBuilderAttribute
100         private String smtpProtocol = "smtp";
101 
102         @PluginBuilderAttribute
103         private String smtpHost;
104 
105         @PluginBuilderAttribute
106         @ValidPort
107         private int smtpPort;
108 
109         @PluginBuilderAttribute
110         private String smtpUsername;
111 
112         @PluginBuilderAttribute(sensitive = true)
113         private String smtpPassword;
114 
115         @PluginBuilderAttribute
116         private boolean smtpDebug;
117 
118         @PluginBuilderAttribute
119         private int bufferSize = DEFAULT_BUFFER_SIZE;
120 
121         @PluginElement("SSL")
122         private SslConfiguration sslConfiguration;
123 
124         /**
125          * Comma-separated list of recipient email addresses.
126          */
127         public Builder setTo(final String to) {
128             this.to = to;
129             return this;
130         }
131 
132         /**
133          * Comma-separated list of CC email addresses.
134          */
135         public Builder setCc(final String cc) {
136             this.cc = cc;
137             return this;
138         }
139 
140         /**
141          * Comma-separated list of BCC email addresses.
142          */
143         public Builder setBcc(final String bcc) {
144             this.bcc = bcc;
145             return this;
146         }
147 
148         /**
149          * Email address of the sender.
150          */
151         public Builder setFrom(final String from) {
152             this.from = from;
153             return this;
154         }
155 
156         /**
157          * Comma-separated list of Reply-To email addresses.
158          */
159         public Builder setReplyTo(final String replyTo) {
160             this.replyTo = replyTo;
161             return this;
162         }
163 
164         /**
165          * Subject template for the email messages.
166          * @see org.apache.logging.log4j.core.layout.PatternLayout
167          */
168         public Builder setSubject(final String subject) {
169             this.subject = subject;
170             return this;
171         }
172 
173         /**
174          * Transport protocol to use for SMTP such as "smtp" or "smtps". Defaults to "smtp".
175          */
176         public Builder setSmtpProtocol(final String smtpProtocol) {
177             this.smtpProtocol = smtpProtocol;
178             return this;
179         }
180 
181         /**
182          * Host name of SMTP server to send messages through.
183          */
184         public Builder setSmtpHost(final String smtpHost) {
185             this.smtpHost = smtpHost;
186             return this;
187         }
188 
189         /**
190          * Port number of SMTP server to send messages through.
191          */
192         public Builder setSmtpPort(final int smtpPort) {
193             this.smtpPort = smtpPort;
194             return this;
195         }
196 
197         /**
198          * Username to authenticate with SMTP server.
199          */
200         public Builder setSmtpUsername(final String smtpUsername) {
201             this.smtpUsername = smtpUsername;
202             return this;
203         }
204 
205         /**
206          * Password to authenticate with SMTP server.
207          */
208         public Builder setSmtpPassword(final String smtpPassword) {
209             this.smtpPassword = smtpPassword;
210             return this;
211         }
212 
213         /**
214          * Enables or disables mail session debugging on STDOUT. Disabled by default.
215          */
216         public Builder setSmtpDebug(final boolean smtpDebug) {
217             this.smtpDebug = smtpDebug;
218             return this;
219         }
220 
221         /**
222          * Number of log events to buffer before sending an email. Defaults to {@value #DEFAULT_BUFFER_SIZE}.
223          */
224         public Builder setBufferSize(final int bufferSize) {
225             this.bufferSize = bufferSize;
226             return this;
227         }
228 
229         /**
230          * Specifies an SSL configuration for smtps connections.
231          */
232         public Builder setSslConfiguration(final SslConfiguration sslConfiguration) {
233             this.sslConfiguration = sslConfiguration;
234             return this;
235         }
236 
237         /**
238          * Specifies the layout used for the email message body. By default, this uses the
239          * {@linkplain HtmlLayout#createDefaultLayout() default HTML layout}.
240          */
241         @Override
242         public Builder setLayout(final Layout<? extends Serializable> layout) {
243             return super.setLayout(layout);
244         }
245 
246         /**
247          * Specifies the filter used for this appender. By default, uses a {@link ThresholdFilter} with a level of
248          * ERROR.
249          */
250         @Override
251         public Builder setFilter(final Filter filter) {
252             return super.setFilter(filter);
253         }
254 
255         @Override
256         public SmtpAppender build() {
257             if (getLayout() == null) {
258                 setLayout(HtmlLayout.createDefaultLayout());
259             }
260             if (getFilter() == null) {
261                 setFilter(ThresholdFilter.createFilter(null, null, null));
262             }
263             final SmtpManager smtpManager = SmtpManager.getSmtpManager(getConfiguration(), to, cc, bcc, from, replyTo,
264                     subject, smtpProtocol, smtpHost, smtpPort, smtpUsername, smtpPassword, smtpDebug,
265                     getFilter().toString(), bufferSize, sslConfiguration);
266             return new SmtpAppender(getName(), getFilter(), getLayout(), smtpManager, isIgnoreExceptions(), getPropertyArray());
267         }
268     }
269 
270     /**
271      * @since 2.13.2
272      */
273     @PluginBuilderFactory
274     public static Builder newBuilder() {
275         return new Builder();
276     }
277 
278     /**
279      * Create a SmtpAppender.
280      * @deprecated Use {@link #newBuilder()} to create and configure a {@link Builder} instance.
281      * @see Builder
282      */
283     public static SmtpAppender createAppender(
284             @PluginConfiguration final Configuration config,
285             @PluginAttribute("name") @Required final String name,
286             @PluginAttribute("to") final String to,
287             @PluginAttribute("cc") final String cc,
288             @PluginAttribute("bcc") final String bcc,
289             @PluginAttribute("from") final String from,
290             @PluginAttribute("replyTo") final String replyTo,
291             @PluginAttribute("subject") final String subject,
292             @PluginAttribute("smtpProtocol") final String smtpProtocol,
293             @PluginAttribute("smtpHost") final String smtpHost,
294             @PluginAttribute(value = "smtpPort", defaultString = "0") @ValidPort final String smtpPortStr,
295             @PluginAttribute("smtpUsername") final String smtpUsername,
296             @PluginAttribute(value = "smtpPassword", sensitive = true) final String smtpPassword,
297             @PluginAttribute("smtpDebug") final String smtpDebug,
298             @PluginAttribute("bufferSize") final String bufferSizeStr,
299             @PluginElement("Layout") Layout<? extends Serializable> layout,
300             @PluginElement("Filter") Filter filter,
301             @PluginAttribute("ignoreExceptions") final String ignore) {
302         if (name == null) {
303             LOGGER.error("No name provided for SmtpAppender");
304             return null;
305         }
306 
307         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
308         final int smtpPort = AbstractAppender.parseInt(smtpPortStr, 0);
309         final boolean isSmtpDebug = Boolean.parseBoolean(smtpDebug);
310         final int bufferSize = bufferSizeStr == null ? DEFAULT_BUFFER_SIZE : Integer.parseInt(bufferSizeStr);
311 
312         if (layout == null) {
313             layout = HtmlLayout.createDefaultLayout();
314         }
315         if (filter == null) {
316             filter = ThresholdFilter.createFilter(null, null, null);
317         }
318         final Configuration configuration = config != null ? config : new DefaultConfiguration();
319 
320         final SmtpManager manager = SmtpManager.getSmtpManager(configuration, to, cc, bcc, from, replyTo, subject, smtpProtocol,
321             smtpHost, smtpPort, smtpUsername, smtpPassword, isSmtpDebug, filter.toString(),  bufferSize, null);
322         if (manager == null) {
323             return null;
324         }
325 
326         return new SmtpAppender(name, filter, layout, manager, ignoreExceptions, null);
327     }
328 
329     /**
330      * Capture all events in CyclicBuffer.
331      * @param event The Log event.
332      * @return true if the event should be filtered.
333      */
334     @Override
335     public boolean isFiltered(final LogEvent event) {
336         final boolean filtered = super.isFiltered(event);
337         if (filtered) {
338             manager.add(event);
339         }
340         return filtered;
341     }
342 
343     /**
344      * Perform SmtpAppender specific appending actions, mainly adding the event
345      * to a cyclic buffer and checking if the event triggers an e-mail to be
346      * sent.
347      * @param event The Log event.
348      */
349     @Override
350     public void append(final LogEvent event) {
351         manager.sendEvents(getLayout(), event);
352     }
353 }