日志同步工具

我们怎么去做日志同步呢?

方案一:在Log4j的体系中有个东西叫做LoggerFilter,这个类的工具是用来做日志过滤,每次我们打印日志的时候都会经过这个filter,来决定是否打印日志。比如:

  public
  int decide(LoggingEvent event) {
    if(this.levelMin != null) {
      if (event.getLevel().isGreaterOrEqual(levelMin) == false) {
        // level of event is less than minimum
        return Filter.DENY;
      }
    }

    if(this.levelMax != null) {
      if (event.getLevel().toInt() > levelMax.toInt()) {
        // level of event is greater than maximum
        // Alas, there is no Level.isGreater method. and using
        // a combo of isGreaterOrEqual && !Equal seems worse than
        // checking the int values of the level objects..
        return Filter.DENY;
      }
    }

    if (acceptOnMatch) {
      // this filter set up to bypass later filters and always return
      // accept if level in range
      return Filter.ACCEPT;
    }
    else {
      // event is ok for this filter; allow later filters to have a look..
      return Filter.NEUTRAL;
    }
  }

可以看到我们在配置文件里面配置的

        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMax" value="ERROR" />
            <param name="LevelMin" value="info" />
        </filter>

根据上面的原理,我们可以定义一个filter,然后每次打印日志的时候异步把日志发送出去。

/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2016 All Rights Reserved.
 */
package com.zhangwei.learning.utils.log;

import java.util.Date;

import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;

import com.zhangwei.learning.model.Constants;
import com.zhangwei.learning.model.LogResource;
import com.zhangwei.learning.model.LogResourceDTO;

/**
 * 日志filter
 * @author Administrator
 * @version $Id: LogFilter.java, v 0.1 2016年7月4日 下午9:41:26 Administrator Exp $
 */
public class LogSendFilter extends Filter implements Constants {

    /** 暂存日志的DTO,当日志内容大小到达sendSize的时候,会把日志发送给服务器 */
    private ThreadLocal<LogResourceDTO> dtoThreadLocal   = new ThreadLocal<LogResourceDTO>() {
                                                             protected LogResourceDTO initialValue() {
                                                                 LogResourceDTO logResourceDTO = new LogResourceDTO();
                                                                 return logResourceDTO;
                                                             };
                                                         };

    /** 日志长度为多少字符的时候会触发发送日志事件 */
    private int                         sendSize         = 100;

    /** 系统名 */
    private String                      systemName       = null;

    /** 用于接收日志的服务器 */
    private String                      logServerAddress = null;

    private LogSender                   logSender        = new LogSender();

    /** 
     * @see org.apache.log4j.spi.Filter#decide(org.apache.log4j.spi.LoggingEvent)
     */
    @Override
    public int decide(LoggingEvent event) {

        initOnEveryTime();

        sendLogs(event);
        return Filter.ACCEPT;
    }

    /**
     * 每次调用Filter得时候都会初始化下
     */
    private void initOnEveryTime() {
        LogResourceDTO dto = dtoThreadLocal.get();
        dto.setSystemName(systemName);
    }

    /**
     * 按照日志条数发送
     * @param event
     * @return
     */
    private void sendLogs(LoggingEvent event) {
        //先检查大小
        String content = event.getMessage() + EMPTY_STRING;
        LogResourceDTO logResourceDTO = dtoThreadLocal.get();

        logResourceDTO.addLogResource(new LogResource(new Date(), event.getLoggerName(), content));
        //当数据没到sendSize那么保存日志
        if (logResourceDTO.getLogs().size() < sendSize) {
            return;
        }
        //日志超sendSize了,那么发送日志,然后把新的日志保存
        //TODO send data
        try {
            logSender.sendLog(logResourceDTO.reflectToString(), logServerAddress);
            logResourceDTO.clear();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        return;
    }

    /**
     * Setter method for property <tt>sendSize</tt>.
     * 
     * @param sendSize value to be assigned to property sendSize
     */
    public void setSendSize(int sendSize) {
        this.sendSize = sendSize;
    }

    /**
     * 设置系统名
     * @param systemName
     */
    public void setSystemName(String systemName) {
        this.systemName = systemName;
    }

    /**
     * Setter method for property <tt>logServerAddress</tt>.
     * 
     * @param logServerAddress value to be assigned to property logServerAddress
     */
    public void setLogServerAddress(String logServerAddress) {
        this.logServerAddress = logServerAddress;
    }
}
日志同步filter

这个filter,实现了如下的功能,使用threadLocal的方式,每个线程都会缓存一个日志DTO对象,发送日志的时候按照条数,达到定义的上限以后,就会把日志发送出去,如果没有达到上限,那就先把日志缓存到本地缓存。

/**
 * 发送日志的task
 * @author Administrator
 * @version $Id: LogSendTask.java, v 0.1 2016年7月4日 下午11:55:21 Administrator Exp $
 */
public class LogSender {

    /** 发送日志的线程池 */
    private ExecutorService THREAD_POOL = Executors
        .newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 3);

    public void sendLog(final String content, final String serverUrl) {
        THREAD_POOL.submit(new Callable<String>() {

            public String call() throws Exception {
                long start = System.currentTimeMillis();
                HttpClientUtil.postData(serverUrl, new HashMap<String, String>() {
                    /**  */
                    private static final long serialVersionUID = 5828324817130371646L;

                    {
                        put(LogResourceDTO.LOG_HTTP_KEY, content);
                    }
                });
                long end = System.currentTimeMillis();
                return (end - start) + "ms";
            }
        });
    }
}

上面采用单独的线程池,将日志发送给指定的服务器,采用的是http协议。

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss:SSS} %c %m%n" />
        </layout> <!--限制输出级别 -->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMax" value="ERROR" />
            <param name="LevelMin" value="info" />
        </filter>
        <filter class="com.zhangwei.learning.utils.log.LogSendFilter">
            <param name="sendSize" value="0" />
            <param name="systemName" value="test" />
            <param name="logServerAddress" value="http://45.62.100.209:8080/DataReceiver/" />
        </filter>
    </appender>
    <root>
        <priority value="debug" />
        <appender-ref ref="CONSOLE" />
    </root>
</log4j:configuration>

filter配置,主要是配置了日志接受服务器,以及本地缓存大小。

缺点分析:1.日志同步在业务主链路上,如果有什么问题会对系统有很大的影响。

原文地址:https://www.cnblogs.com/color-my-life/p/5816538.html