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.core.Appender; 021import org.apache.logging.log4j.core.Core; 022import org.apache.logging.log4j.core.Filter; 023import org.apache.logging.log4j.core.Layout; 024import org.apache.logging.log4j.core.LogEvent; 025import org.apache.logging.log4j.core.config.Configuration; 026import org.apache.logging.log4j.core.config.DefaultConfiguration; 027import org.apache.logging.log4j.core.config.Property; 028import org.apache.logging.log4j.core.config.plugins.Plugin; 029import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 030import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 031import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 032import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 033import org.apache.logging.log4j.core.config.plugins.PluginElement; 034import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 035import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort; 036import org.apache.logging.log4j.core.filter.ThresholdFilter; 037import org.apache.logging.log4j.core.layout.HtmlLayout; 038import org.apache.logging.log4j.core.net.SmtpManager; 039import org.apache.logging.log4j.core.net.ssl.SslConfiguration; 040import org.apache.logging.log4j.core.util.Booleans; 041 042import java.io.Serializable; 043 044/** 045 * Send an e-mail when a specific logging event occurs, typically on errors or 046 * fatal errors. 047 * 048 * <p> 049 * The number of logging events delivered in this e-mail depend on the value of 050 * <b>BufferSize</b> option. The <code>SmtpAppender</code> keeps only the last 051 * <code>BufferSize</code> logging events in its cyclic buffer. This keeps 052 * memory requirements at a reasonable level while still delivering useful 053 * application context. 054 * 055 * By default, an email message will formatted as HTML. This can be modified by 056 * setting a layout for the appender. 057 * 058 * By default, an email message will be sent when an ERROR or higher severity 059 * message is appended. This can be modified by setting a filter for the 060 * appender. 061 */ 062@Plugin(name = "SMTP", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 063public final class SmtpAppender extends AbstractAppender { 064 065 private static final int DEFAULT_BUFFER_SIZE = 512; 066 067 /** The SMTP Manager */ 068 private final SmtpManager manager; 069 070 private SmtpAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 071 final SmtpManager manager, final boolean ignoreExceptions, final Property[] properties) { 072 super(name, filter, layout, ignoreExceptions, properties); 073 this.manager = manager; 074 } 075 076 /** 077 * @since 2.13.2 078 */ 079 public static class Builder extends AbstractAppender.Builder<Builder> 080 implements org.apache.logging.log4j.core.util.Builder<SmtpAppender> { 081 @PluginBuilderAttribute 082 private String to; 083 084 @PluginBuilderAttribute 085 private String cc; 086 087 @PluginBuilderAttribute 088 private String bcc; 089 090 @PluginBuilderAttribute 091 private String from; 092 093 @PluginBuilderAttribute 094 private String replyTo; 095 096 @PluginBuilderAttribute 097 private String subject; 098 099 @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}