乐趣区

关于java:老生常谈SpringAop日志收集与处理做的工具包

AopLog 是基于 Spring Aop 和 ThreadLocal 实现的一个专门对申请办法内容日志的拦挡与解决的日志工具包。

场景 :

  1. 应用 Spring Aop 拦挡参数日志目前大部分做法都基本上大同小异, 不想日后每个我的项目工程都写一份这样的 Aop 拦挡解决日志的代码, 甚至代码侵入。
  2. 我想晓得一些绝对重要的申请办法的申请参数, 响应参数, 申请头, 以及外部耗时, 办法是胜利还是失败等等信息。产生谬误时我也不晓得执行到哪一步产生了异样,是不是某个参数导致出的逻辑问题。
  3. 一般的 log.info 或 warn 信息没有所属申请的高低关系, 并不不便查看和剖析。
  4. 正式环境中, 我并不想打印太多无意义的 info 日志(有些只是为了排查问题打印的日志, 程序失常运行时其实毫无意义),只心愿在产生异样时记录日志或者只心愿每次申请只记录一条次要害的申请信息。
  5. 日志的收集, 我心愿将这些申请的日志记录下来,记录的实现形式我本人决定,比方失常的日志打印,常见的日志写入数据库,日志写入到文件,日志入队列等等。
  6. 整个日志的记录齐全不烦扰失常申请办法的流程, 日志的收集解决异步化, 齐全不影响失常申请办法的性能与响应。
  7. 只须要通过 @AopLog 注解决定是否记录。

疾速开始

我的项目通过 maven 的 pom.xml 引入


<dependency>
    <groupId>com.github.ealenxie</groupId>
    <artifactId>aop-log</artifactId>
    <version>2.1</version>
</dependency>

或者通过 gradle 引入

compile group: 'com.github.ealenxie', name: 'aop-log', version: '2.1'

@AopLog 注解应用,进行日志记录

间接在类 (作用类的所有办法) 或类办法 (作用于办法) 上加上注解 @AopLog, 进行日志记录

例如 :

import com.github.AopLog;
import name.ealen.infra.base.resp.RespBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author EalenXie create on 2020/6/22 14:28
 */
@AopLog(type = "测试",stackTraceOnErr = true)
@RestController
public class AppController {@GetMapping("/app/sayHello")
    public RespBody<String> sayHello() {return RespBody.ok("hello EalenXie");
    }

}

自定义全局的日志收集器实现收集 LogCollector

例如只是简略打印, 或写入到库等等。


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.LogData;
import com.github.collector.LogCollector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author EalenXie create on 2020/9/15 13:46
 * 此为样例参考
 * 配置一个简略的日志收集器 这里只是做了一个 log.info 打印一下,能够在这里写入到数据库中或者写入
 */
@Slf4j
@Component
public class AopLogCollector implements LogCollector {private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public void collect(LogData logData) {
        try {log.info(objectMapper.writeValueAsString(logData));
        } catch (JsonProcessingException e) {e.printStackTrace();
        }
    }
}

配置 @Component 的全局日志收集器只能配置一个。

接口调用 /say/hello 测试即可看看到控制台打印出后果 :

2020-09-16 16:01:04.782  INFO 2012 --- [AsyncExecutor-2] name.ealen.infra.advice.AopLogCollector  : {"appName":"app-template","host":"127.0.0.1","port":8080,"clientIp":"192.168.110.1","reqUrl":"http://localhost:8080/app/sayHello","httpMethod":"GET","headers":{"User-Agent":"Apache-HttpClient/4.5.10 (Java/11.0.5)"},"type":"测试","content":"","method":"name.ealen.api.facade.AppController#sayHello","args":null,"respBody":{"code":"200","desc":"OK","message":" 申请胜利 ","dateTime":"2020-09-16 16:01:04","body":"hello EalenXie"},"logDate":1600243264780,"costTime":1,"threadName":"http-nio-8080-exec-3","threadId":33,"success":true}

记录的日志对象 LogData 属性阐明

LogData 记录的内容

| 字段 | 类型 | 正文 |
| :——- | :———— | :—– |
| appName | String | 利用名称 |
| host | String | 主机 |
| port | int | 端口号 |
| clientIp | String | 申请客户端的 Ip |
| reqUrl | String | 申请地址 |
| headers | Object | 申请头部信息(可抉择记录) 默认记录 user-agent,content-type |
| type | String | 操作类型, 默认值 undefined |
| content | String | 办法步骤内容, 默认是空, 可应用 LogData.step 进行内容步骤记录 |
| method | String | 申请的本地 java 办法 |
| args | Object | 办法申请参数 |
| respBody | Object | 办法响应参数 |
| costTime | long | 整个办法耗时 |
| logDate | Date | Log 产生工夫,LogData 对象初始化的工夫 |
| threadName | String | 线程名称 |
| threadId | long | 线程 Id |
| success | boolean | 执行状态, 胜利(true)/ 异样(false) |

AopLog 注解选项阐明

选项 类型 阐明 默认
logOnErr boolean 仅当产生异样时才记录收集 false
type String 操作类型 默认值 ”undefined”
headers String[] 记录的 header 信息 , 抉择要记录哪些 header 信息 默认 ”User-Agent”,”content-type”
args boolean 是否记录申请参数 true
respBody boolean 是否记录响应参数 true
stackTraceOnErr boolean 当指标办法产生异样时, 是否追加异样堆栈信息到 LogData 的 content 中 false
asyncMode boolean 异步形式收集 true
collector Class<? extends LogCollector> 指定日志收集器 默认不调整收集器, 应用全局的日志收集器

LogData 的 step 办法。

记录步骤。(如果某些重要步骤心愿被记录下来)
例如 :

import com.github.AopLog;
import com.github.LogData;
import name.ealen.infra.base.resp.RespBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


/**
 * @author EalenXie create on 2020/6/22 14:28
 */
@AopLog(type = "测试",stackTraceOnErr = true)
@RestController
public class AppController {@GetMapping("/app/sayHello")
    public RespBody<String> sayHello() {LogData.step("1. 第一步执行实现");
        //......
        LogData.step("2. 第二步执行实现");
        //.....
        LogData.step("3. service 的办法执行实现");
        //.....
        return RespBody.ok("hello EalenXie");
    }

}

留神: 此办法如果不在被 @AopLog 注解的办法的整体调用链路中应用,则以后线程中的 ThreadLocal 中的 LogData 不会开释,须要手动调用 LogData.removeCurrent();

此时再次接口调用 /say/hello 测试即可看看到控制台打印出后果,重点察看 content 字段 :

2020-09-16 17:26:20.285  INFO 3284 --- [AsyncExecutor-2] name.ealen.infra.advice.AopLogCollector  : {"appName":"app-template","host":"127.0.0.1","port":8080,"clientIp":"192.168.110.1","reqUrl":"http://localhost:8080/app/sayHello","httpMethod":"GET","headers":{"User-Agent":"Apache-HttpClient/4.5.10 (Java/11.0.5)"},"type":"测试","content":"1. 第一步执行实现 \n2. 第二步执行实现 \n3. service 的办法执行实现 \n","method":"name.ealen.api.facade.AppController#sayHello","args":null,"respBody":{"code":"200","desc":"OK","message":"申请胜利","dateTime":"2020-09-16 17:26:20","body":"hello EalenXie"},"logDate":1600248380283,"costTime":1,"threadName":"http-nio-8080-exec-2","threadId":32,"success":true}

对于

开源 Github 地址 : [https://github.com/EalenXie/a…
](https://github.com/EalenXie/a…

感激各位提出意见和反对。

退出移动版