zoukankan      html  css  js  c++  java
  • 日志同步工具

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

    方案一:在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.日志同步在业务主链路上,如果有什么问题会对系统有很大的影响。

  • 相关阅读:
    CopyOnWriteArrayList设计思路与源码分析
    点击页面按钮以excel保存到本地
    上传图片
    关于重复点击的
    去首尾空格还有换行问题//把数字换位大写字母//向后台传输数据
    判断输入的时间与当前的时间(判断时间是今天还是以前的)
    前端的一些小技巧
    git 操作大全
    移动web开发常见问题解决方案
    响应式布局
  • 原文地址:https://www.cnblogs.com/color-my-life/p/5816538.html
Copyright © 2011-2022 走看看