我们怎么去做日志同步呢?
方案一:在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.日志同步在业务主链路上,如果有什么问题会对系统有很大的影响。
时间: 2024-10-22 02:27:40