logHelp日志代码分析

在公司的项目中,有一个日志模块,代码写的很好,效率比较高,而且能支持高并发,频繁写日志的系统,拿来分析分析。
首先贴个代码块;

public class LogHelp extends Thread {

    private static Logger logger = Logger.getLogger(LogHelp.class);

    /** 设置线程的检查时间间隔 */
    private long interval = 1000L;

    /**
     * 保存 list对象的队列. 每个对象的初始容量MAX_COUNT, 新加入的list对象被添加到队尾,
     * currentList指示当前正在操作的list对象
     */
    public static ConcurrentLinkedQueue<List<String>> imprQueue = new ConcurrentLinkedQueue<List<String>>();

    /** list中最大个数 8192 */
    private static int MAX_COUNT = 8192;


    /** 当前正在处理的当前的 list对象 */
    private static List<String> currentList = new ArrayList<String>(MAX_COUNT);

    /** 临时引用 */
    private List<String> tempList = new ArrayList<String>();

    /** 上次从queue中取出list去保存的时间点 */
    private static long lastSaveTimePoint = System.currentTimeMillis();

    /**
     * 两次保存的最大时间间隔, 若某两次取出的时间间隔超过这个值, 无论数量是否到达, 都取出一个list去保存. 单位: 毫秒.
     * 来自配置项impr.queue.interval.max
     */
    private static long MAX_SAVE_INTERVAL = 1000 * 10;

    /**
     * 线程锁参数, true:可执行 false:不可执行
     */
    private static boolean canOperate = true;

    public void run() {
        while (true) {
            try {
                // 读取配置文件信息,设置线程的检查时间间隔
                interval = 1000;

                // 读取配置文件信息,设置imprQueue队列中list的最大容量
                MAX_COUNT = 8192;


                // 读取配置文件信息,设置imprQueue队列两次取出list的最大时间间隔
                MAX_SAVE_INTERVAL = 20000;

                try {
                    synchronized (this) {
                        // 防止陷入无限循环, 等待1秒后,启动下一次入库操作
                        wait(interval);
                    }
                } catch (InterruptedException e) { // 退出出口,
                    // 收到InterruptedException异常即退出
                    System.err
                            .println(">> InterruptedException occurred to exit, please restart ClickLogThread");
                    return;
                }
                // 主方法
                saveLog();

            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 定时将内存中的广告位监控日志数据, 批量写入数据库
     */
    private void saveLog() {
        // (1)时间的判断 超时的入库
        if (currentList.size() > 0
                && System.currentTimeMillis() - lastSaveTimePoint >= MAX_SAVE_INTERVAL) {
            listIntoQueue();
        }

        // (2)队列入库
        tempList = getNextListToSave();
        while (tempList != null) {
            printLog(tempList);
            tempList.clear();
            tempList = getNextListToSave();
        }
    }

    /**
     * list进队列(有锁的操作)
     */
    private static void listIntoQueue() {
        if (canOperate) {
            canOperate = false;
            try {
                imprQueue.offer(currentList); // '装填已满的list'被加到队列尾部
                currentList = new ArrayList<String>(MAX_COUNT);
                lastSaveTimePoint = System.currentTimeMillis();
            } catch (Exception e) {
                e.printStackTrace();
            }
            canOperate = true;
        }
    }

    /**
     * 取出队列头部的list, 该list元素数量已经达到MAX_COUNT; 如果数量没有到达MAX_COUNT或没有队列, 返回null
     * 
     * @return 队列头部的元素数量已经达到MAX_COUNT的list; 可能为null
     */
    private List<String> getNextListToSave() {
        if (imprQueue.size() > 0) {
            return imprQueue.poll();
        } else {
            return null;
        }
    }

    /**
     * 输出日志
     * 
     * @param list
     */
    private void printLog(List<String> list) {
        if (list == null || list.size() == 0)
            return;
        //
        else {
            for (String s : list) {
                logger.info(s);
            }
        }
    }

    /** 把一个对象保存到内存list中去 */
    public static void saveLogInMemory(String str) {
        if (str == null || StringUtils.isEmpty(str)) {
            return;
        }
        // 如果currentList中的元素个数到达MAX_COUNT, 或者queue中没有list而最大时间间隔已到达, 则准备下一个list
        if (currentList.size() >= MAX_COUNT) {
            listIntoQueue();
        }
        currentList.add(str);
    }

}

log4j配置文件

log4j.rootLogger=INFO,dailyFile
log4j.additivity.org.apache=true

#console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=INFO
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.Target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyyMMdd:HH:mm:ss,SSS} - %c -%-4r %-5p --> %m%n

#dailyFile
log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyFile.Threshold=DEBUG
log4j.appender.dailyFile.ImmediateFlush=true
log4j.appender.dailyFile.Append=true
log4j.appender.dailyFile.File=/mnt/dev/opt/rxlog/dspbrain.log
log4j.appender.dailyFile.DatePattern='.'yyyyMMdd_HH-mm
log4j.appender.dailyFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyFile.layout.ConversionPattern=%d{yyyyMMdd\:HH\:mm\:ss,SSS} - %c -%-4r %-5p --> %m%n
#log4j.appender.dailyFile.BufferedIO=true
#log4j.appender.dailyFile.BufferSize=8192

在tomcat启动时,开启日志线程

@WebListener
public class StartListener implements ServletContextListener{

    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("servlet context 初始化");
        System.out.println(sce.getServletContext().getServerInfo());
        new LogHelp().start();
    }

    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("servlet context 销毁");
    }

}

这个loghelp中相当于新开了一条专门的线程,用来记录日志。
1、首先在tomcat启动的时候,开启日志线程,对外暴露的保存日志的方法是 saveLogInMemory(String str);
2、这个日志的线程是一个while(true);无线循环,然后线程会等待一秒钟,也就是会一秒钟循环执行一次。循环队列的时候,会做两个操作
a.检查currentList是否有值,且是否超过10秒钟,满足条件,入队列
b.检查tempList是否有值,满足条件,弹出队列

所以,队列相当于一个大池子,currentList相当于不断从外面接水往池子里倒水,templist相当于不断从队列这个大池子里舀水向外撒。队列这个大池子有个监控系统,控制向里面倒水,和 向外面舀水。
loghelp程序图解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值