001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017
018package org.apache.logging.log4j.core.appender;
019
020import org.apache.logging.log4j.Level;
021import org.apache.logging.log4j.core.Filter;
022import org.apache.logging.log4j.core.Layout;
023import org.apache.logging.log4j.core.LogEvent;
024import org.apache.logging.log4j.core.config.plugins.Plugin;
025import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
026import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
027import org.apache.logging.log4j.core.config.plugins.PluginElement;
028import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
029import org.apache.logging.log4j.core.filter.ThresholdFilter;
030import org.apache.logging.log4j.core.layout.HtmlLayout;
031import org.apache.logging.log4j.core.net.SmtpManager;
032import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
033import org.apache.logging.log4j.core.util.Booleans;
034
035import java.io.Serializable;
036
037/**
038 * Send an e-mail when a specific logging event occurs, typically on errors or
039 * fatal errors.
040 *
041 * <p>
042 * The number of logging events delivered in this e-mail depend on the value of
043 * <b>BufferSize</b> option. The <code>SmtpAppender</code> keeps only the last
044 * <code>BufferSize</code> logging events in its cyclic buffer. This keeps
045 * memory requirements at a reasonable level while still delivering useful
046 * application context.
047 *
048 * By default, an email message will formatted as HTML. This can be modified by
049 * setting a layout for the appender.
050 *
051 * By default, an email message will be sent when an ERROR or higher severity
052 * message is appended. This can be modified by setting a filter for the
053 * appender.
054 */
055@Plugin(name = "SMTP", category = "Core", elementType = "appender", printObject = true)
056public final class SmtpAppender extends AbstractAppender {
057
058    private static final long serialVersionUID = 1L;
059    private static final int DEFAULT_BUFFER_SIZE = 512;
060
061    /** The SMTP Manager */
062    private final SmtpManager manager;
063
064    private SmtpAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, final SmtpManager manager,
065                         final boolean ignoreExceptions) {
066        super(name, filter, layout, ignoreExceptions);
067        this.manager = manager;
068    }
069
070    public static class Builder implements org.apache.logging.log4j.core.util.Builder<SmtpAppender> {
071        @PluginBuilderAttribute
072        @Required(message = "No name provided for SmtpAppender")
073        private String name;
074
075        @PluginElement("Filter")
076        private Filter filter = ThresholdFilter.createFilter(null, null, null);
077
078        @PluginElement("Layout")
079        private Layout<? extends Serializable> layout = HtmlLayout.createDefaultLayout();
080
081        @PluginBuilderAttribute
082        private boolean ignoreExceptions = true;
083
084        @PluginBuilderAttribute
085        private String to;
086
087        @PluginBuilderAttribute
088        private String cc;
089
090        @PluginBuilderAttribute
091        private String bcc;
092
093        @PluginBuilderAttribute
094        private String from;
095
096        @PluginBuilderAttribute
097        private String replyTo;
098
099        @PluginBuilderAttribute
100        private String subject;
101
102        @PluginBuilderAttribute
103        private String smtpProtocol = "smtp";
104
105        @PluginBuilderAttribute
106        private String smtpHost;
107
108        @PluginBuilderAttribute
109        private int smtpPort;
110
111        @PluginBuilderAttribute
112        private String smtpUsername;
113
114        @PluginBuilderAttribute(sensitive = true)
115        private String smtpPassword;
116
117        @PluginBuilderAttribute
118        private boolean smtpDebug;
119
120        @PluginBuilderAttribute
121        private int bufferSize = DEFAULT_BUFFER_SIZE;
122
123        @PluginElement("SSL")
124        private SslConfiguration sslConfiguration;
125
126        /**
127         * Name of the SmtpAppender.
128         */
129        public Builder setName(final String name) {
130            this.name = name;
131            return this;
132        }
133
134        /**
135         * Overrides the default filter. Default filter is a {@link ThresholdFilter} of level {@link Level#ERROR}.
136         */
137        public Builder setFilter(final Filter filter) {
138            this.filter = filter;
139            return this;
140        }
141
142        /**
143         * Overrides the default layout. Default layout is the {@linkplain HtmlLayout#createDefaultLayout() default HTML layout}.
144         */
145        public Builder setLayout(final Layout<? extends Serializable> layout) {
146            this.layout = layout;
147            return this;
148        }
149
150        /**
151         * Whether to ignore exceptions thrown by the SmtpAppender. Defaults to {@code true}.
152         */
153        public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
154            this.ignoreExceptions = ignoreExceptions;
155            return this;
156        }
157
158        /**
159         * Comma-separated list of recipient email addresses.
160         */
161        public Builder setTo(final String to) {
162            this.to = to;
163            return this;
164        }
165
166        /**
167         * Comma-separated list of CC email addresses.
168         */
169        public Builder setCc(final String cc) {
170            this.cc = cc;
171            return this;
172        }
173
174        /**
175         * Comma-separated list of BCC email addresses.
176         */
177        public Builder setBcc(final String bcc) {
178            this.bcc = bcc;
179            return this;
180        }
181
182        /**
183         * Email address of the sender.
184         */
185        public Builder setFrom(final String from) {
186            this.from = from;
187            return this;
188        }
189
190        /**
191         * Comma-separated list of Reply-To email addresses.
192         */
193        public Builder setReplyTo(final String replyTo) {
194            this.replyTo = replyTo;
195            return this;
196        }
197
198        /**
199         * Subject template for the email messages.
200         * @see org.apache.logging.log4j.core.layout.PatternLayout
201         */
202        public Builder setSubject(final String subject) {
203            this.subject = subject;
204            return this;
205        }
206
207        /**
208         * Transport protocol to use for SMTP such as "smtp" or "smtps". Defaults to "smtp".
209         */
210        public Builder setSmtpProtocol(final String smtpProtocol) {
211            this.smtpProtocol = smtpProtocol;
212            return this;
213        }
214
215        /**
216         * Host name of SMTP server to send messages through.
217         */
218        public Builder setSmtpHost(final String smtpHost) {
219            this.smtpHost = smtpHost;
220            return this;
221        }
222
223        /**
224         * Port number of SMTP server to send messages through.
225         */
226        public Builder setSmtpPort(final int smtpPort) {
227            this.smtpPort = smtpPort;
228            return this;
229        }
230
231        /**
232         * Username to authenticate with SMTP server.
233         */
234        public Builder setSmtpUsername(final String smtpUsername) {
235            this.smtpUsername = smtpUsername;
236            return this;
237        }
238
239        /**
240         * Password to authenticate with SMTP server.
241         */
242        public Builder setSmtpPassword(final String smtpPassword) {
243            this.smtpPassword = smtpPassword;
244            return this;
245        }
246
247        /**
248         * Enables or disables mail session debugging on STDOUT. Disabled by default.
249         */
250        public Builder setSmtpDebug(final boolean smtpDebug) {
251            this.smtpDebug = smtpDebug;
252            return this;
253        }
254
255        /**
256         * Number of log events to buffer before sending an email. Defaults to {@value #DEFAULT_BUFFER_SIZE}.
257         */
258        public Builder setBufferSize(final int bufferSize) {
259            this.bufferSize = bufferSize;
260            return this;
261        }
262
263        /**
264         * Specifies an SSL configuration for smtps connections.
265         */
266        public Builder setSslConfiguration(final SslConfiguration sslConfiguration) {
267            this.sslConfiguration = sslConfiguration;
268            return this;
269        }
270
271        @Override
272        public SmtpAppender build() {
273            if (layout == null) {
274                layout = HtmlLayout.createDefaultLayout();
275            }
276            if (filter == null) {
277                filter = ThresholdFilter.createFilter(null, null, null);
278            }
279            final SmtpManager smtpManager =
280                    SmtpManager.getSMTPManager(to, cc, bcc, from, replyTo, subject, smtpProtocol, smtpHost, smtpPort,
281                            smtpUsername, smtpPassword, smtpDebug, filter.toString(), bufferSize, sslConfiguration);
282            return new SmtpAppender(name, filter, layout, smtpManager, ignoreExceptions);
283        }
284    }
285
286    @PluginBuilderFactory
287    public static Builder newBuilder() {
288        return new Builder();
289    }
290
291    @Deprecated
292    public static SmtpAppender createAppender(final String name, final String to, final String cc, final String bcc,
293            final String from, final String replyTo, final String subject, final String smtpProtocol, final String smtpHost,
294            final String smtpPortStr, final String smtpUsername, final String smtpPassword, final String smtpDebug,
295            final String bufferSizeStr, Layout<? extends Serializable> layout, Filter filter, final String ignore) {
296        if (name == null) {
297            LOGGER.error("No name provided for SmtpAppender");
298            return null;
299        }
300
301        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
302        final int smtpPort = AbstractAppender.parseInt(smtpPortStr, 0);
303        final boolean isSmtpDebug = Boolean.parseBoolean(smtpDebug);
304        final int bufferSize = bufferSizeStr == null ? DEFAULT_BUFFER_SIZE : Integer.parseInt(bufferSizeStr);
305
306        if (layout == null) {
307            layout = HtmlLayout.createDefaultLayout();
308        }
309        if (filter == null) {
310            filter = ThresholdFilter.createFilter(null, null, null);
311        }
312
313        final SmtpManager manager = SmtpManager.getSMTPManager(to, cc, bcc, from, replyTo, subject, smtpProtocol,
314            smtpHost, smtpPort, smtpUsername, smtpPassword, isSmtpDebug, filter.toString(),  bufferSize);
315        if (manager == null) {
316            return null;
317        }
318
319        return new SmtpAppender(name, filter, layout, manager, ignoreExceptions);
320    }
321
322    /**
323     * Capture all events in CyclicBuffer.
324     * @param event The Log event.
325     * @return true if the event should be filtered.
326     */
327    @Override
328    public boolean isFiltered(final LogEvent event) {
329        final boolean filtered = super.isFiltered(event);
330        if (filtered) {
331            manager.add(event);
332        }
333        return filtered;
334    }
335
336    /**
337     * Perform SmtpAppender specific appending actions, mainly adding the event
338     * to a cyclic buffer and checking if the event triggers an e-mail to be
339     * sent.
340     * @param event The Log event.
341     */
342    @Override
343    public void append(final LogEvent event) {
344        manager.sendEvents(getLayout(), event);
345    }
346}