日誌同步工具

咱們怎麼去作日誌同步呢?java

方案一:在Log4j的體系中有個東西叫作LoggerFilter,這個類的工具是用來作日誌過濾,每次咱們打印日誌的時候都會通過這個filter,來決定是否打印日誌。好比:apache

  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對象,發送日誌的時候按照條數,達到定義的上限之後,就會把日誌發送出去,若是沒有達到上限,那就先把日誌緩存到本地緩存。app

/**
 * 發送日誌的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協議。less

<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.日誌同步在業務主鏈路上,若是有什麼問題會對系統有很大的影響。ide

相關文章
相關標籤/搜索