log4j想必大家都很熟了,它是apache组织旗下的一个开源项目,用于记录程序运行时的一些信息,这些信息有助于我们分析程序的性能找出bug。log4j共有六个级别,按级别升序排序分别是
TRACK | 记录比debug级别还要详细的信息(finer-grained 细颗粒度) |
DEBUG | 记录程序运行时一些有用的信息,用来分析程序的性能,发现bug(finer-grained 细颗粒度) |
INFO | 记录程序运行时正常输出的提示信息(coarse-grained 粗颗粒度) |
WARN | 指明程序运行状态处在欠佳状态,可能会出现异常情况。 |
ERROR | 指明程序发生了错误,但还可以允许程序继续运行。 |
FATAL | 指明程序发生了非常重大的错误,并引导程序停止运行。 |
一般情况下这几个级别已经够用,但在一些情况下并不能满足我们的要求,例如在我开发的自动跟踪定位程序中,要每间隔10秒针钟查询一次数据库,取出当前时间需要定位的终端然后发起定位,每定一次位需要从账户中扣除一定的费用,如果费用被扣完,则要向客服及市场部发送提醒邮件,提醒xxx企业已经欠费,请联系该企业及时充值。这实现这样的功能,这几个级别显然不能满足我们的要求的,所以要自定义一个级别,专门用来发送提醒邮件的
1、自定义级别类
package com.tdt.log4j.extend; import org.apache.log4j.Level; /** * @project MRMAutoloc * @author sunnylocus * @vresion 1.0 2009-7-22 * @description 自定义级别REMIND,该级别用来发送提醒邮件,级别要比INFO低 */ public class TDTLevel extends Level { private static final long serialVersionUID = 7288304330257085144L; static public final int REMIND_INT = Level.INFO_INT - 1; static public final int LETHAL_INT = Level.FATAL_INT + 1; private static String REMIND_STR = "REMIND"; private static String LETHAL_STR = "LETHAL"; public static final TDTLevel REMIND = new TDTLevel(REMIND_INT, REMIND_STR,7); public static final TDTLevel LETHAL = new TDTLevel(LETHAL_INT, LETHAL_STR,0); protected TDTLevel(int level, String strLevel, int syslogEquiv) { super(level, strLevel, syslogEquiv); } /** * Convert the string passed as argument to a level. If the conversion * fails, then this method returns {@link #REMIND}. */ public static Level toLevel(String sArg) { return (Level) toLevel(sArg, TDTLevel.REMIND); } public static Level toLevel(String sArg, Level defaultValue) { if (sArg == null) { return defaultValue; } String stringVal = sArg.toUpperCase(); if (stringVal.equals(REMIND_STR)) { return TDTLevel.REMIND; } else if (stringVal.equals(LETHAL_STR)) { return TDTLevel.LETHAL; } return Level.toLevel(sArg, (Level) defaultValue); } public static Level toLevel(int i) throws IllegalArgumentException { switch (i) { case REMIND_INT: return TDTLevel.REMIND; case LETHAL_INT: return TDTLevel.LETHAL; } return Level.toLevel(i); } }
写好自定义级别,还要作邮件发送策略的处理
2、邮件发送处理类
package com.tdt.log4j.extend; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.TriggeringEventEvaluator; import com.tdt.util.DateUtil; public final class MockTriggeringEventEvaluator { //处理程序出错邮件提醒 public final static class ErrorMockTriggeringEventEvaluator implements TriggeringEventEvaluator{ private static boolean isSended = false;//是否已发送邮件 private static String senedTime ="";//发送时间 @Override public boolean isTriggeringEvent(LoggingEvent arg0) { if(!isSended) { isSended = true;////标记邮件已发送 senedTime = DateUtil.currentTime(); Logger.getLogger(getClass()).info("已发送程序出错提醒邮件!"); return true; } String currentTime = DateUtil.currentTime(); if(DateUtil.calculateTimeoutByMinute(senedTime, currentTime) > 10) { //距上次发送邮件已超过10分名钟,再次发送邮件 return true; } return false; } } //处理企业欠费邮件提醒 public final static class RemindMockTriggeringEventEvaluator implements TriggeringEventEvaluator{ public boolean isTriggeringEvent(LoggingEvent arg0) { Logger.getLogger(getClass()).info("已发送企业欠费提醒邮件!"); return true; //欠费邮件不作处理,直接发送 } } }
3、日期工具类
package com.tdt.util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * @project MRMAutoloc * @author sunnylocus * @vresion 1.0 2009-7-13 * @description 日期工具类,提供日期的加减及比较 */ public class DateUtil { /** *@return String 当前时间(格式yyyyMMddHHmmss) */ public static String currentTime(){ SimpleDateFormat formate = new SimpleDateFormat("yyyyMMddHHmmss"); Calendar calendar = Calendar.getInstance(); return formate.format(calendar.getTime()); } /** * 计算两个时间之间相差多少分钟 * @param pretiem 之前的时间 (格式:yyyyMMddHHmmss) * @param currentTime 现在的时间 (格式:yyyyMMddHHmmss) * @return int 相差多少分钟 */ public static int calculateTimeoutByMinute(String pretiem,String currentTime) { try { //设置时间 SimpleDateFormat formate = new SimpleDateFormat("yyyyMMddHHmmss"); Calendar calendar = Calendar.getInstance(); calendar.setTime(formate.parse(pretiem)); int pre =calendar.get(Calendar.MINUTE); calendar.setTime(formate.parse(currentTime)); int curr = calendar.get(Calendar.MINUTE); return (curr - pre); } catch (ParseException e) { throw new IllegalArgumentException(e); } } }
测试
package com.tdt.test; import org.apache.log4j.Logger; import com.tdt.log4j.extend.TDTLevel; public class TestSendMail { static Logger log = Logger.getLogger(TestSendMail.class); public static void main(String[] args) { log.log(TDTLevel.REMIND, "测试,xxx,企业欠费,该企业所属终端的定位请求将被过滤"); log.error("测试,程序运行发生了一个错误"); log.error("测试,程序运行发生了一个错误"); } }
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<!-- 企业欠费邮件提醒 ,发送至客服,市场部-->
<appender name="LOG.REMIND_SMTP" class="org.apache.log4j.net.SMTPAppender">
<param name="from" value="[email protected]" />
<param name="to" value="[email protected],[email protected],[email protected]" /><!--多个邮件地址用逗号分割-->
<param name="subject" value="企业欠费通知" />
<param name="SMTPHost" value="mail.tdt-lbs.com" />
<triggeringPolicy class="com.tdt.log4j.extend.MockTriggeringEventEvaluator$RemindMockTriggeringEventEvaluator" /> <!--邮件发送策略,自已定义-->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%m%n" />
</layout>
<!-- 级别过滤 -->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="REMIND#com.tdt.log4j.extend.TDTLevel" /> <!--在这里添加我们自定义的级别,注意#号不能少 -->
<param name="LevelMax" value="REMIND#com.tdt.log4j.extend.TDTLevel" />
</filter>
</appender>
<!-- 程序出错邮件提醒,发送至程序开发人员 -->
<appender name="LOG.ERROR_SMTP" class="org.apache.log4j.net.SMTPAppender">
<param name="from" value="[email protected]" />
<param name="to" value="[email protected]" />
<param name="subject" value="MRM平台自动跟踪定位程序出错" />
<param name="SMTPHost" value="mail.tdt-lbs.com" />
<triggeringPolicy
class="com.tdt.log4j.extend.MockTriggeringEventEvaluator$ErrorMockTriggeringEventEvaluator" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %c{2} - %m%n" />
</layout>
<!-- 级别过滤 -->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="ERROR" />
<param name="LevelMax" value="FATAL" />
</filter>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="LOG.REMIND_SMTP" />
<appender-ref ref="LOG.ERROR_SMTP" />
</root>
</log4j:configuration>
然后在log4j.xml配置我们自定义的级别。大家最好用xml配置log4j的参数,因为properties的方式已经过时,log4j不推荐使用。
收到两封邮件
对错误邮件的发送,间隔控制在10分钟,如果不作控制一旦程序(多线程情况下)出错,每个线程会发送相同内容的邮件,会把你的信箱填满。
对邮件正文出现乱码解决的方案
将org.apache.log4j.Layout类
public String getContentType() {
return "text/plain";
}
改为
public String getContentType() {
return "text/plain;charset=UTF-8";
}
然后重新编译,将编译后的类替换原来的class文件
要实现发送邮件功能需要在classpath设置mail.jar文件。我已经打包上传