在开发过程中,我们有时需要将重要的错误日志通过邮件发送给相关的责任人,这样能即时发现错误,即时解决。如使用Log4J,一般会做如下配置:

log4j.rootLogger = debug,mail

# 发送日志到指定邮件
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
log4j.appender.mail.Threshold=INFO
log4j.appender.mail.BufferSize=10
log4j.appender.mail.From=from@qq.com
log4j.appender.mail.To=to@163.com
log4j.appender.mail.SMTPHost=smtp.qq.com
#发送邮件箱的用户
log4j.appender.mail.SMTPUsername=from@qq.com
#邮箱的授权码
log4j.appender.mail.SMTPPassword=

但是我在使用过程中发现标准的org.apache.log4j.net.SMTPAppender有如下几个问题。

  1. 同步发送邮件。这样会阻塞业务正常进行(比如等待一个SQL查询,需要等待邮件发送后才显示结果,显然不能忍受)

解决办法: 使用线程池的方式,将发送邮件包装成Runnable任务,送到线程池中执行。同步队列我选择的是设置了固定大小的LinkedBlockingQueue,设置固定大小是因为需要发邮件的重要日志不是太多,二是不能因为邮件任务占用了太多的内存;选择LinkedBlockingQueue,是因为LinkedBlockingQueue读写锁分离,可以边添加任务,边发送邮件;核心线程数和最大线程数,可以根据业务量和CPU核数设定。

  1. 缓存大小bufferSize(日志事件的个数)只是设置缓存大小,并不能等到缓存满时才发送(其实是只要有发生ERROR级别及以上的的事件时就将缓存中保存的所有满足threshold级别的日志都发送,在发送之前缓存满时会从头开始,新的日志覆盖旧的)

解决办法: 去掉默认实现类CyclicBuffer,改成同步队列;因为CyclicBuffer线程不安全,添加日志和获取日志并不是同一个线程,所以采用了线程安全的同步队列,而且还需要实现当同步队列中日志快满时将触发发送邮件;所以需要自定义同步队列,加上一个阀值factor,当同步队列中的日志个数达到bufferSize*factor时就发送邮件,这样可以预留一部分空间存放后添加进来的日志;同步队列我选择的是 LinkedBlockingQueue,可以边添加日志,边读取日志,吞吐量比较大。目前发送邮件触发的条件是:发生了 ERRORERROR以上级别 的错误时发送邮件,改成当缓存同步队列中元素个数大于或等于 bufferSize*factor 时,触发回调函数,启动发送邮件任务。

改造步骤:

  1. 定义回调接口AlertWillBeFull
  2. 自定义同步队列 AlertLinkedBlockingQueue ,继承 LinkedBlockingQueue ,添加成员变量factor及回调接口,在所有添加动作之前进行判断是否达到阀值。
  3. 去掉SMTPAppender类中的实现类DefaultEvaluator及所有调用它的地方
  4. 在创建缓存同步队列时,传入回调对象,等待同步队列调用
  5. 添加日志到缓存同步队列和从缓存同步队列读取日志分别使用offer和poll方法,不阻塞线程也不抛异常,以免影响实际业务进行,而且少少量日志影响也不大。
  1. 发送的日志比较杂乱,需要排除某些包下的日志(比如有些不重要的日志,或者只想看某些包下的日志

解决方法:添加成员变量excludePackageincludePackage,修改checkEntryConditions方法逻辑

这样就能在log4j.properties配置文件中配置缓存大小,以及添加排除或只关心的记录日志的包,也可以添加多个发送邮件的配置,将不同包下的日志发送给不同的责任人。

最终配置如下:

log4j.rootLogger = debug,mail

# 发送日志到指定邮件
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
#排除的包(多个包,以英文逗号隔开)
#log4j.appender.mail.excludePackage=com.alibaba.druid
#仅关心的包,一般excludePackage与includePackage任选一即可,多个包以英文逗号隔开
log4j.appender.mail.includePackage=cn.yang.practise.service,cn.yang.practise.controller,com.alibaba.druid
log4j.appender.mail.Threshold=INFO
log4j.appender.mail.BufferSize=16
log4j.appender.mail.From=from@qq.com
log4j.appender.mail.To=to@163.com
log4j.appender.mail.SMTPHost=smtp.qq.com
#发送邮件箱的用户
log4j.appender.mail.SMTPUsername=from@qq.com
#邮箱的授权码
log4j.appender.mail.SMTPPassword=

改造后完整后的org.apache.log4j.net.SMTPAppenderAlertLinkedBlockingQueue

Log4j的邮件发送类SMTPAppender改造的更多相关文章

  1. PHP 邮件发送类

    mail.php <?php /** * 邮件发送类 * 支持发送纯文本邮件和HTML格式的邮件,可以多收件人,多抄送,多秘密抄送,带附件的邮件 * 需要的php扩展,sockets和Filei ...

  2. C#邮件发送类 简单实用 可自定义发件人名称

    上图看效果 MailHelper: public class MailHelper { public bool SendMail(MailSender sender,out string errorM ...

  3. asp.net 邮件发送类

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  4. 关于 PHPMailer 邮件发送类的使用心得(含多文件上传)

    This is important for send mail PHPMailer 核心文件 class.phpmailer.php class.phpmaileroauth.php class.ph ...

  5. 关于.NET 的邮件发送类

    .NET 类库中已经有现成的封好的类库了,只要引用System.Net.Mail命名空间即可实现发邮件的功能 以下是代码 public class SendMail { private string ...

  6. 利用phpmailer类邮件发送

    <?php require("class.phpmailer.php"); //下载的文件必须放在该文件所在目录 $mail = new PHPMailer(); //建立邮 ...

  7. 【Thinkphp 5】 整合邮箱类 phpmailer实现邮件发送

    第一步:下载phpmailer文件,主要用到的文件只有箭头指向的两个,thinkphp5中,把class.phpmailer.php改成了phpmailer.php 第二步: 将phpmailer文件 ...

  8. 一个用于发送HTML格式邮件的类

    以下类是在网上孙钰佳的版本上改写而来,主要变化了三点:1.去掉了附件部分:2.形式从纯Java类改成可注入方式:3.to,cc和bcc都变成了一堆人,以前是一个人. 以下是Java类的代码: impo ...

  9. J2EE 邮件发送那些事儿

    距离自己写的关于java邮件发送的第一篇博客已经有很长一段时间了,现在回过头看看.虽然代码质量方面有待提高,整体结构也不怎样,但是基本思路和过程还是比较纯的.现在有空写写J2EE中邮件发送的开发,实际 ...

随机推荐

  1. A - Playground

    My kid's school cleared a large field on their property recently to convert it into a playing area.  ...

  2. 用NginX+keepalived实现高可用的负载均衡

    Table of Contents 1 规划和准备 2 安装 3 配置 3.1 配置NginX 3.2 配置keepalived 3.3 让keepalived监控NginX的状态 4 还可以做什么 ...

  3. Poetry

    1. Absence to love is what wind is to fire. It extinguishes the small; It inflames the great. 2. It ...

  4. ES6之Array.from()方法

    Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组. 那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象. 1.将类数组对象转换为真正数 ...

  5. 一步步Cobol 400上手自学入门教程05 - 表

    在COBOL中有几类典型结构的表.这几类典型结构的表在大体上可分为下标表和索引表两大类.另外,根据表的重复次数定义又有定长表和变长表.此外,表还允许嵌套,因此还有嵌套表.这几类表均符合表的基本定义,都 ...

  6. elasticsearch5.6.3插件部署

    需要注意的是,5.x和2.x插件方面改动很大.参考:https://www.elastic.co/blog/running-site-plugins-with-elasticsearch-5-0.因为 ...

  7. Linux 学习错误点整理之网络配置

    本人是一名实习生,最近在学习Linux,在实操的过程中还是遇到了一些问题,所以想记录下来,供自己以后复习,也希望能给跟我一样的菜鸟的人带来一点点帮助. 我用的是VMware Workstation P ...

  8. html头部标签大全

    http://www.css88.com/archives/8052#table-index

  9. WebForm - cookie赋值乱码问题

    cookie的值为中文时候,取cookie的值会出现乱码 解决办法:存取cookie时候先解码和编码 存cookie,进行编码: cookie.Value = HttpUtility.UrlEncod ...

  10. odoo开发笔记-日期时间相关操作

    日期格式化字符串:DATE_FORMAT = "%Y-%m-%d" 日期时间格式字符串:DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" ...