Monday, May 16, 2011

Log4j SMTPAppender and deadlocks - Adding Timeout support

On the post: Sending Email alerts with Log4j we saw how we can easily send mails whenever our system has an exception. But we have to remember that using Apache Log4j SMTPAppender, can be very risky if not used cautiously. In general, the Log4j SMTPAppender has 2 main problems:

  1. The part that send the mail, is synchronous. In fact, it is synchronous between all Log4j appenders. It means that when you log an error and a mail is being sent, all log commands are locked. This is quite risky. Especially when your system has many errors (for whatever reason). This may cause your whole system to get stuck. This issue can be easily handled by using: AsyncAppender. I may write about AsyncAppender in more detail in the future.
  2. The code responsible for sending the mail, which is written by Apache developers, doesn't has a Timeout. That mean, a mail being sent can be stuck forever and simply cause your entire system to go into a deadlock.
    In this post, we will improve the Apache SMTPAppender to include a Timeout property. The Timout property will make sure, that if a mail is being sent using SMTP connection for too long, it will be dropped. We may loose a report about an exception, but we will make sure our system won't be stuck indefinitely.


We will create a new class name: SMTPAppenderTimeout, that extends SMTPAppender. This new class will override the SMTPAppender method: createSession.
The new createSession method will make sure to add the following 2 properties to the mail session: 

  • mail.smtp.connectiontimeout
  • mail.smtp.timeout


These 2 properties instruct the Java mail framework to set a timeout on the SMTP connection.
Let's have a look on the SMTPAppenderTimeout class:

package com.bashan.blog; 
import org.apache.log4j.net.SMTPAppender; 
import javax.mail.Authenticator; 
import javax.mail.PasswordAuthentication; 
import javax.mail.Session; 
import java.util.Properties; 
/** 
* @author Bashan 
*/ 
public class SMTPAppenderTimeout extends SMTPAppender { 
  private int timeout; 
  public int getTimeout() { 
    return timeout; 
  } 
  public void setTimeout(int timeout) { 
    this.timeout = timeout; 
  } 
  @Override 
  protected Session createSession() { 
    Properties props; 
    try { 
      props = new Properties(System.getProperties()); 
    } catch (SecurityException ex) { 
      props = new Properties(); 
    } 
    if (timeout > 0) { 
      String timeoutStr = Integer.toString(timeout); 
      props.setProperty("mail.smtp.connectiontimeout", timeoutStr); 
      props.setProperty("mail.smtp.timeout", timeoutStr); 
    } 
    if (getSMTPHost() != null) { 
      props.put("mail.smtp.host", getSMTPHost()); 
    } 
    Authenticator auth = null; 
    if (getSMTPPassword() != null && getSMTPUsername() != null) { 
      props.put("mail.smtp.auth", "true"); 
      auth = new Authenticator() { 
        protected PasswordAuthentication getPasswordAuthentication() { 
          return new PasswordAuthentication(getSMTPUsername(), getSMTPPassword()); 
        } 
      }; 
    } 
    Session session = Session.getInstance(props, auth); 
    if (getSMTPDebug()) { 
      session.setDebug(getSMTPDebug()); 
    } 
    return session; 
  } 
} 

You can also download the SMTPAppenderTimeout.



Let's see an example of a log4j.xml file which use SMTPAppenderTimeout to allow timeout of 5 seconds (5000ms):       


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	
	<appender name="RollFile" class="org.apache.log4j.RollingFileAppender">
		<param name="File" value="C:\\testlog.txt" />
		<param name="MaxFileSize" value="10MB" />
		<param name="MaxBackupIndex" value="1" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{HH:mm:ss} %-5p [%c{1}]: %m%n" />
		</layout>
	</appender>
	<appender name="Email" class="com.bashan.blog.SMTPAppenderTimeout">
		<param name="BufferSize" value="10" />
		<param name="SMTPHost" value="smtpout.secureserver.net" />
		<param name="SMTPUsername" value="test_user" />
		<param name="SMTPPassword" value="test_password" />
		<param name="Timeout" value="5000" />
		<param name="From" value="someone@mail.com" />
		<param name="To" value="bashan@mail.com" />
		<param name="Subject" value="System Error Notification" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d [%t] %-5p %c %x - %m%n" />
		</layout>
	</appender>
	
	<root>
		<priority value="info" />
		<appender-ref ref="RollFile" />
		<appender-ref ref="Email" />
	</root>
</log4j:configuration>

You can also download the log4j.xml.




No comments:

Post a Comment