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