美文网首页Dubbo
Dubbo源码分析----过滤器之AccessLogFilter

Dubbo源码分析----过滤器之AccessLogFilter

作者: _六道木 | 来源:发表于2018-07-17 00:57 被阅读84次

    dubbo的accessLog可以记录请求信息,配置如下:

    <dubbo:protocol accesslog="true" />
    

    然后配置一下日志文件就OK了,具体百度一下,这里只分析一下其中的原理。

    accessLog的处理是在filter中,具体为AccessLogFilter,看下类的定义

    @Activate(group = Constants.PROVIDER, value = Constants.ACCESS_LOG_KEY)
    public class AccessLogFilter implements Filter {
    //....
    }
    

    类上有Activate注解,其中有两个属性,一个是group,值为provider,代表该filter对provider有效;另外一个是value,值为accesslog,根据扩展机制,代表当url中有accesslog属性,那么该Filter就会被激活,加入到过滤器链中。

    接下来,分析一下调用过程,Filter触发入口是invoke方法

        public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
            try {
                String accesslog = invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
                if (ConfigUtils.isNotEmpty(accesslog)) {//判断accesslog属性是否有值
                    //从上下文中获取调用信息,拼装成字符串
                    RpcContext context = RpcContext.getContext();
                    String serviceName = invoker.getInterface().getName();
                    String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
                    String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
                    StringBuilder sn = new StringBuilder();
                    sn.append("[").append(new SimpleDateFormat(MESSAGE_DATE_FORMAT).format(new Date())).append("] ").append(context.getRemoteHost()).append(":").append(context.getRemotePort())
                    .append(" -> ").append(context.getLocalHost()).append(":").append(context.getLocalPort())
                    .append(" - ");
                    if (null != group && group.length() > 0) {
                        sn.append(group).append("/");
                    }
                    sn.append(serviceName);
                    if (null != version && version.length() > 0) {
                        sn.append(":").append(version);
                    }
                    sn.append(" ");
                    sn.append(inv.getMethodName());
                    sn.append("(");
                    Class<?>[] types = inv.getParameterTypes();
                    if (types != null && types.length > 0) {
                        boolean first = true;
                        for (Class<?> type : types) {
                            if (first) {
                                first = false;
                            } else {
                                sn.append(",");
                            }
                            sn.append(type.getName());
                        }
                    }
                    sn.append(") ");
                    Object[] args = inv.getArguments();
                    if (args != null && args.length > 0) {
                        sn.append(JSON.json(args));
                    }
                    String msg = sn.toString();
                    // 字符串拼装完成,判断accesslog的值走两个流程
                    if (ConfigUtils.isDefault(accesslog)) {
                        LoggerFactory.getLogger(ACCESS_LOG_KEY + "." + invoker.getInterface().getName()).info(msg);
                    } else {
                        log(accesslog, msg);
                    }
                }
            } catch (Throwable t) {//ERROR}
            return invoker.invoke(inv);
        }
    

    逻辑简单,最后判断accesslog的值如果是true或者default就直接调用日志框架进行写日志,如果不是则走另外的流程

    这种情况下,accesslog属性是一个文件的地址,那么看下是如何处理的

        private void log(String accesslog, String logmessage) {
            init();// 初始化
            Set<String> logSet = logQueue.get(accesslog);// 以文件路径为key获取集合
            if (logSet == null) {
                logQueue.putIfAbsent(accesslog, new ConcurrentHashSet<String>());
                logSet = logQueue.get(accesslog);
            }
            if (logSet.size() < LOG_MAX_BUFFER) {// 集合上限数量是LOG_MAX_BUFFER
                logSet.add(logmessage);
            }
        }
    

    首先调用init方法初始化,然后将日志信息放到了一个集合当中,以文件名为key,区分不同的日志,那么很显然,会有一个线程去获取集合中的数据,看下init方法

        private void init() {
            if (logFuture == null) {
                synchronized (logScheduled) {
                    if (logFuture == null) {
                        logFuture = logScheduled.scheduleWithFixedDelay(new LogTask(), LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
                    }
                }
            }
        }
    

    初始化了一个定时器,5s执行一次LogTask任务,LogTask里应该就是取队列写日志的地方,看下其run方法逻辑

        for (Map.Entry<String, Set<String>> entry : logQueue.entrySet()) {
            try {
                String accesslog = entry.getKey();
                Set<String> logSet = entry.getValue();
                File file = new File(accesslog);
                File dir = file.getParentFile();
                if (null!=dir&&! dir.exists()) {
                    dir.mkdirs();
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Append log to " + accesslog);
                }
                if (file.exists()) {
                    String now = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date());
                    String last = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date(file.lastModified()));
                    if (! now.equals(last)) {
                        File archive = new File(file.getAbsolutePath() + "." + last);
                        file.renameTo(archive);
                    }
                }
                FileWriter writer = new FileWriter(file, true);
                try {
                    for(Iterator<String> iterator = logSet.iterator();
                        iterator.hasNext();
                        iterator.remove()) {
                        writer.write(iterator.next());
                        writer.write("\r\n");
                    }
                    writer.flush();
                } finally {
                    writer.close();
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    

    非常简单,就是创建日志文件,写集合中的字符串,即日志写入文件中

    相关文章

      网友评论

        本文标题:Dubbo源码分析----过滤器之AccessLogFilter

        本文链接:https://www.haomeiwen.com/subject/ocvipftx.html