首页 > 代码库 > 日志同步工具

日志同步工具

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

方案一:在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="http://www.mamicode.com/ERROR" />            <param name="LevelMin" value="http://www.mamicode.com/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.日志同步在业务主链路上,如果有什么问题会对系统有很大的影响。

日志同步工具