LOG4J用了common logging的标准接口。。。所以用LOG4J的时候得引用common logging的包哦。。
当然如果要在LOG4J中用EMAIL的话,得把javamail和jaf包引用进来。。。
OK。。原理我就不啰嗦了。。直接上代码和配置。
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class TestLog {
private static Log log = LogFactory.getLog(TestLog.class);
public void test() {
log.debug("This is the debug message.");
log.info("This is the info message.");
log.warn("This is the warn message.");
log.error("This is the error message.");
log.fatal("This is the fatal message.");
}
public static void main(String[] args) {
TestLog testLog = new TestLog();
testLog.test();
}
}
配置:
log4j.rootLogger = info,stdout,RF,MAIL #There is 3 appender,you can use any of them according to you requirement. #Set the variable pattern=Happened Time[%d] Log Type[%p](%F:%L) - %m%n #appender1[name=stdout] log to console log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern =${pattern} #appender2[name=RF] log to file log4j.appender.RF = org.apache.log4j.RollingFileAppender log4j.appender.RF.File = d:/logs/pfingoopenapi2.log log4j.appender.RF.MaxFileSize = 10000KB log4j.appender.RF.MaxBackupIndex = 1 log4j.appender.RF.layout=org.apache.log4j.PatternLayout log4j.appender.RF.layout.ConversionPattern=${pattern} #appender3[name=MAIL] log to email log4j.appender.MAIL = com.gftech.log4j.SMTPExtAppender log4j.appender.MAIL.Threshold=ERROR log4j.appender.MAIL.BufferSize=512 log4j.appender.MAIL.SMTPHost=smtp.qq.com log4j.appender.MAIL.to=wangking717@qq.com log4j.appender.MAIL.from=wangking717@qq.com #custom by myself[using auth] log4j.appender.MAIL.SMTPAuth=true #custom by myself[using auth] log4j.appender.MAIL.SMTPUsername=wangking717 #custom by myself[using auth] log4j.appender.MAIL.SMTPPassword=your password here log4j.appender.MAIL.Subject=Log4J Message log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout log4j.appender.MAIL.layout.ConversionPattern=${pattern}
因为大部分的邮件服务器都是需要认证的,而LOG4J大牛们却没有把认证加进去。。那么就只有自己来改进它吧。。
由于SMTPAppender的属性都是私有的,没有给我们留下继承的可能,所以我们选择直接从它的父类AppenderSkeleton来继承,然后把SMTPAppender的源代码全部COPY过来,增加如下EMAIL用户名、密码和是否需要认证的标记:
// Define auth info
private String smtpUsername;
private String smtpPassword;
private String smtpAuth;
在activateOptions()方法中增加验证的代码:
/* ADD auth code */
if (smtpAuth != null && smtpAuth.trim().equals("true")) ...{
props.put("mail.smtp.auth", "true");
authenticator = new Authenticator() ...{
protected PasswordAuthentication getPasswordAuthentication() ...{
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
};
}
// Session session = Session.getInstance(props, null);
Session session = Session.getInstance(props, authenticator);
如果发送的日志信息涉及到中文字符,还必须设定发送内容的编码,在sendBuffer()方法如修改设置content为如下所示,把编码方式指定为GB2312格式:
part.setContent(sbuf.toString(), layout.getContentType()+";charset=GB2312");
最后添加三个新加属性的GETTER和SETTER,全部源代码如下所示:
package com.gftech.log4j;
import java.util.Date;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.TriggeringEventEvaluator;
/**
* SMTP Appender扩展,增加对邮件认证的支持
*
* @author lenovo
*
*/
public class SMTPExtAppender extends AppenderSkeleton {
private String to;
private String from;
private String subject;
private String smtpHost;
// define auth info
private String smtpUsername;
private String smtpPassword;
private String smtpAuth;
// --------------------------
private int bufferSize = 512;
private boolean locationInfo = false;
protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
protected Message msg;
protected TriggeringEventEvaluator evaluator;
/**
* The default constructor will instantiate the appender with a
* {@link TriggeringEventEvaluator} that will trigger on events with level
* ERROR or higher.
*/
public SMTPExtAppender() {
this(new DefaultEvaluator());
}
/**
* Use <code>evaluator</code> passed as parameter as the {@link
* TriggeringEventEvaluator} for this SMTPAppender.
*/
public SMTPExtAppender(TriggeringEventEvaluator evaluator) {
this.evaluator = evaluator;
}
/**
* Activate the specified options, such as the smtp host, the recipient,
* from, etc.
*/
public void activateOptions() {
Properties props = new Properties(System.getProperties());
Authenticator authenticator = null;
if (smtpHost != null)
props.put("mail.smtp.host", smtpHost);
/**//* ADD auth code */
if (smtpAuth != null && smtpAuth.trim().equals("true")) {
props.put("mail.smtp.auth", "true");
authenticator = new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername,
smtpPassword);
}
};
}
// Session session = Session.getInstance(props, null);
Session session = Session.getInstance(props, authenticator);
// session.setDebug(true);
msg = new MimeMessage(session);
try {
if (from != null)
msg.setFrom(getAddress(from));
else
msg.setFrom();
msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
if (subject != null)
msg.setSubject(subject);
} catch (MessagingException e) {
LogLog.error("Could not activate SMTPAppender options.", e);
}
}
/**
* Perform SMTPAppender specific appending actions, mainly adding the event
* to a cyclic buffer and checking if the event triggers an e-mail to be
* sent.
*/
public void append(LoggingEvent event) {
if (!checkEntryConditions()) {
return;
}
event.getThreadName();
event.getNDC();
if (locationInfo) {
event.getLocationInformation();
}
cb.add(event);
if (evaluator.isTriggeringEvent(event)) {
sendBuffer();
}
}
/**
* This method determines if there is a sense in attempting to append.
*
* <p>
* It checks whether there is a set output target and also if there is a set
* layout. If these checks fail, then the boolean value <code>false</code>
* is returned.
*/
protected boolean checkEntryConditions() {
if (this.msg == null) {
errorHandler.error("Message object not configured.");
return false;
}
if (this.evaluator == null) {
errorHandler
.error("No TriggeringEventEvaluator is set for appender ["
+ name + "].");
return false;
}
if (this.layout == null) {
errorHandler.error("No layout set for appender named [" + name
+ "].");
return false;
}
return true;
}
synchronized public void close() {
this.closed = true;
}
InternetAddress getAddress(String addressStr) {
try {
return new InternetAddress(addressStr);
} catch (AddressException e) {
errorHandler.error("Could not parse address [" + addressStr + "].",
e, ErrorCode.ADDRESS_PARSE_FAILURE);
return null;
}
}
InternetAddress[] parseAddress(String addressStr) {
try {
return InternetAddress.parse(addressStr, true);
} catch (AddressException e) {
errorHandler.error("Could not parse address [" + addressStr + "].",
e, ErrorCode.ADDRESS_PARSE_FAILURE);
return null;
}
}
/**
* Returns value of the <b>To</b> option.
*/
public String getTo() {
return to;
}
/**
* The <code>SMTPAppender</code> requires a
* {@link org.apache.log4j.Layout layout}.
*/
public boolean requiresLayout() {
return true;
}
/**
* Send the contents of the cyclic buffer as an e-mail message.
*/
protected void sendBuffer() {
// Note: this code already owns the monitor for this
// appender. This frees us from needing to synchronize on 'cb'.
try {
MimeBodyPart part = new MimeBodyPart();
StringBuffer sbuf = new StringBuffer();
String t = layout.getHeader();
if (t != null)
sbuf.append(t);
int len = cb.length();
for (int i = 0; i < len; i++) {
// sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
LoggingEvent event = cb.get();
sbuf.append(layout.format(event));
if (layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
for (int j = 0; j < s.length; j++) {
sbuf.append(s[j]);
}
}
}
}
t = layout.getFooter();
if (t != null)
sbuf.append(t);
part.setContent(sbuf.toString(), layout.getContentType()
+ ";charset=GB2312");
Multipart mp = new MimeMultipart();
mp.addBodyPart(part);
msg.setContent(mp);
msg.setSentDate(new Date());
Transport.send(msg);
} catch (Exception e) {
LogLog.error("Error occured while sending e-mail notification.", e);
}
}
/**
* Returns value of the <b>EvaluatorClass</b> option.
*/
public String getEvaluatorClass() {
return evaluator == null ? null : evaluator.getClass().getName();
}
/**
* Returns value of the <b>From</b> option.
*/
public String getFrom() {
return from;
}
/**
* Returns value of the <b>Subject</b> option.
*/
public String getSubject() {
return subject;
}
/**
* The <b>From</b> option takes a string value which should be a e-mail
* address of the sender.
*/
public void setFrom(String from) {
this.from = from;
}
/**
* The <b>Subject</b> option takes a string value which should be a the
* subject of the e-mail message.
*/
public void setSubject(String subject) {
this.subject = subject;
}
/**
* The <b>BufferSize</b> option takes a positive integer representing the
* maximum number of logging events to collect in a cyclic buffer. When the
* <code>BufferSize</code> is reached, oldest events are deleted as new
* events are added to the buffer. By default the size of the cyclic buffer
* is 512 events.
*/
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
cb.resize(bufferSize);
}
/**
* The <b>SMTPHost</b> option takes a string value which should be a the
* host name of the SMTP server that will send the e-mail message.
*/
public void setSMTPHost(String smtpHost) {
this.smtpHost = smtpHost;
}
/**
* Returns value of the <b>SMTPHost</b> option.
*/
public String getSMTPHost() {
return smtpHost;
}
/**
* The <b>To</b> option takes a string value which should be a comma
* separated list of e-mail address of the recipients.
*/
public void setTo(String to) {
this.to = to;
}
/**
* Returns value of the <b>BufferSize</b> option.
*/
public int getBufferSize() {
return bufferSize;
}
/**
* The <b>EvaluatorClass</b> option takes a string value representing the
* name of the class implementing the {@link TriggeringEventEvaluator}
* interface. A corresponding object will be instantiated and assigned as
* the triggering event evaluator for the SMTPAppender.
*/
public void setEvaluatorClass(String value) {
evaluator = (TriggeringEventEvaluator) OptionConverter
.instantiateByClassName(value, TriggeringEventEvaluator.class,
evaluator);
}
/**
* The <b>LocationInfo</b> option takes a boolean value. By default, it is
* set to false which means there will be no effort to extract the location
* information related to the event. As a result, the layout that formats
* the events as they are sent out in an e-mail is likely to place the wrong
* location information (if present in the format).
*
* <p>
* Location information extraction is comparatively very slow and should be
* avoided unless performance is not a concern.
*/
public void setLocationInfo(boolean locationInfo) {
this.locationInfo = locationInfo;
}
/**
* Returns value of the <b>LocationInfo</b> option.
*/
public boolean getLocationInfo() {
return locationInfo;
}
public String getSMTPAuth() {
return smtpAuth;
}
/**
* 设置是否进行SMTP认证。
*/
public void setSMTPAuth(String smtpAuth) {
this.smtpAuth = smtpAuth;
}
/**
* Returns value of the <b>SMTPPassword</b> option.
*
* @return <b>SMTPPassword</b>
*/
public String getSMTPPassword() {
return smtpPassword;
}
/**
* 设置访问SMTP服务器的密码。
*/
public void setSMTPPassword(String smtpPassword) {
this.smtpPassword = smtpPassword;
}
/**
* Returns value of the <b>SMTPUsername</b> option.
*
* @return <b>SMTPUsername</b>
*/
public String getSMTPUsername() {
return smtpUsername;
}
/**
* 设置访问SMTP服务器的用户名。
*/
public void setSMTPUsername(String smtpUsername) {
this.smtpUsername = smtpUsername;
}
}
class DefaultEvaluator implements TriggeringEventEvaluator {
/**
* Is this <code>event</code> the e-mail triggering event?
*
* <p>
* This method returns <code>true</code>, if the event level has ERROR
* level or higher. Otherwise it returns <code>false</code>.
*/
public boolean isTriggeringEvent(LoggingEvent event) {
return event.getLevel().isGreaterOrEqual(Level.ERROR);
}
}