基于AOP和ThreadLocal实现的一个日志记录的例子

主要功能实现 : 在API每次被请求时,可以在整个方法调用链路中记录一条唯一的API请求日志,可以记录请求中绝大部分关键内容。并且可以自定义实现对日志收集(直接标准输出,或写入到文件或数据库)。

比如传参,响应,请求url,请求方法,clientIp,耗时,请求成功或异常,请求头等等。

实现的核心为AOP以及ThreadLocal。

  • AOP 会切所有被@Log4a注解的方法,会记录一个线程中唯一一个Log4对象,读取AOP中的方法信息(入参,方法等等)
  • 抓取请求的内容和HttpServletRequest中的内容,解析入参。
  • 日志收集(自定义实现,建议该过程异步)
  • 记录无论目标方法成功或失败,在执行完成后都将对ThreadLocal中的资源进行释放。

Log4 记录的内容

字段 类型 注释 是否默认记录
clientIp String 请求客户端的Ip
reqUrl String 请求地址
headers Object 请求头部信息(可选择记录) 是,默认记录user-agent,content-type
type String 操作类型 是,默认值undefined
content StringBuilder 步骤内容信息 否,方法内容,可使用Log4.step进行内容步骤记录

Log4a 注解选项说明

字段 类型 注释 默认
type String 操作类型 默认值"undefined"
method boolean 是否记录请求的本地java方法 true
costTime boolean 是否记录整个方法耗时 true
headers String[] 记录的header信息 默认"User-Agent","content-type"
args boolean 是否记录请求参数 true
respBody boolean 是否记录响应参数 true
stackTrace boolean 当目标方法发生异常时,是否追加异常堆栈信息到content false
costTime boolean 是否记录整个方法耗时 true
collector Class<? extends LogCollector> 指定日志收集器 默认空的收集器不指定

例子使用说明

@Log4a注解使用

直接在Controller 方法或类上加上注解@Log4a,可以对该Controller中所有方法进行日志记录与收集

例如 :


@Log4a(type = "测试API", stackTrace = true)
@RestController
public class DemoController {
@Resource
private DemoService demoService;
/**
* JSON数据测试
*/
@PostMapping("/sayHello")
public ResponseEntity<?> sayHello(@RequestBody Map<String, Object> request) {
demoService.sayHello(request);
return ResponseEntity.ok(request);
}
/**
* RequestParam 参数测试
*/
@PostMapping("/params")
public ResponseEntity<?> params(@RequestParam Integer a) {
return ResponseEntity.ok(a);
}
/**
* 无参测试
*/
@GetMapping("/noArgs")
public ResponseEntity<?> noArgs() {
return ResponseEntity.ok().build();
}
/**
* XML 格式数据测试
*/
@PostMapping(value = "/callXml", consumes = {MediaType.APPLICATION_XML_VALUE})
public XmlDataDTO callXml(@RequestBody XmlDataDTO dataDTO) {
return dataDTO;
}
/**
* 特殊对象测试
*/
@GetMapping("/callHttpServletRequest")
public ResponseEntity<?> callHttpServletRequest(HttpServletRequest request) {
return ResponseEntity.ok().build();
}
}
Log4.step 记录详细步骤内容

这里调用了service方法,Log4.step 方法记录每一个步骤详细内容

/**
* @author EalenXie Created on 2020/1/16 10:49.
*/
@Service
@Slf4j
public class DemoService {
/**
* 测试方法, 使用Log4.step记录步骤
*/
public void sayHello(Map<String, Object> words) {
Log4.step("1. 请求来了,执行业务动作");
log.info("do somethings");
Log4.step("2. 业务动作执行完成");
}
}
自定义的全局日志收集器

本例中写了一个最简单的直接append写入到文件中,你可以选择自定义的方式进行日志收集(例如写入到数据库或者日志文件,或日志收集框架中,这个过程建议异步处理,可在collect方法上面加入注解@Async)


@Component
public class DemoLogCollector implements LogCollector { @Override
public void collect(Log4 log4) throws LogCollectException {
try {
File file = new File("D:\\home\\temp\\日志.txt");
if (!file.getParentFile().exists()) {
FileUtils.forceMkdir(file.getParentFile());
}
try (FileWriter fw = new FileWriter(file, true)) {
fw.append(log4.toString());
}
} catch (IOException e) {
throw new LogCollectException(e);
}
}
}

测试后 , 可以从 D:\home\temp\日志.txt中获取到记录的日志内容。

json格式的数据记录(参数JSON):
{
"args": {
"id": 999,
"value": "content"
},
"clientIp": "192.168.1.54",
"content": "1. 请求来了,执行业务动作\n2. 业务动作执行完成\n",
"costTime": 2,
"headers": {
"User-Agent": "Apache-HttpClient/4.5.10 (Java/11.0.5)",
"Content-Type": "application/json"
},
"logDate": 1593341797293,
"method": "name.ealen.demo.controller.DemoController#sayHello",
"reqUrl": "http://localhost:9527/sayHello",
"respBody": {
"headers": {},
"statusCodeValue": 200,
"body": {
"id": 999,
"value": "content"
},
"statusCode": "OK"
},
"success": true,
"type": "测试API"
}
XML格式的数据(参数XML):

{
"args": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><xml><message>1111 </message><username>zhangsan</username></xml>",
"clientIp": "192.168.1.54",
"content": "",
"costTime": 4,
"headers": {
"User-Agent": "Apache-HttpClient/4.5.10 (Java/11.0.5)",
"Content-Type": "application/xml"
},
"logDate": 1593394523000,
"method": "name.ealen.demo.controller.DemoController#callXml",
"reqUrl": "http://localhost:9527/callXml",
"respBody": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><xml><message>1111 </message><username>zhangsan</username></xml>",
"success": true,
"type": "测试API"
}
form参数格式的数据(以参数键值对形式):

{
"args": "z=11&a=1",
"clientIp": "192.168.1.54",
"content": "",
"costTime": 1,
"headers": {
"User-Agent": "Apache-HttpClient/4.5.10 (Java/11.0.5)",
"Content-Type": "application/x-www-form-urlencoded"
},
"logDate": 1593342114342,
"method": "name.ealen.demo.controller.DemoController#params",
"reqUrl": "http://localhost:9527/params",
"respBody": {
"headers": {},
"statusCodeValue": 200,
"body": 1,
"statusCode": "OK"
},
"success": true,
"type": "测试API"
}

特殊参数格式(目前暂为键值对形式,参数默认取对象的toString()方法):

{
"args": "request=org.apache.catalina.connector.RequestFacade@754f30c3",
"clientIp": "192.168.1.54",
"content": "",
"costTime": 1,
"headers": {
"User-Agent": "Apache-HttpClient/4.5.10 (Java/11.0.5)"
},
"logDate": 1593342220880,
"method": "name.ealen.demo.controller.DemoController#callHttpServletRequest",
"reqUrl": "http://localhost:9527/callHttpServletRequest",
"respBody": {
"headers": {},
"statusCodeValue": 200,
"body": null,
"statusCode": "OK"
},
"success": true,
"type": "测试API"
}

Github项目地址 :https://github.com/EalenXie/Log4a

目前暂时项目命名为Log4a(Log for API), 有时间会一直维护和优化。

基于AOP和ThreadLocal实现日志记录的更多相关文章

  1. 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块

    Log4a 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块 github地址 : https://github.com/EalenXie/log4a 在API每次被请求时 ...

  2. 来一手 AOP 注解方式进行日志记录

    系统日志对于定位/排查问题的重要性不言而喻,相信许多开发和运维都深有体会. 通过日志追踪代码运行状况,模拟系统执行情况,并迅速定位代码/部署环境问题. 系统日志同样也是数据统计/建模的重要依据,通过分 ...

  3. 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil

    封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...

  4. 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,nloglogutil

    封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...

  5. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  6. 利用AOP与ToStringBuilder简化日志记录

    刚学spring的时候书上就强调spring的核心就是ioc和aop blablabla...... IOC到处都能看到...AOP么刚开始接触的时候使用在声明式事务上面..当时书上还提到一个用到ao ...

  7. springboot整合aop实现网站访问日志记录

    目的: 统一日志输出格式,统计访问网站的ip. 思路: 1.针对不同的调用场景定义不同的注解,目前想的是接口层和服务层. 2.我设想的接口层和服务层的区别在于: (1)接口层可以打印客户端IP,而服务 ...

  8. spring aop通过注解实现日志记录

    首先是几个概念:连接点(Joinpoint).切点(Pointcut).增强(Advice).切面(Aspect) 另外也要使用到注解. 需求:通过注解定义LogEnable.然后程序运行能够识别定义 ...

  9. 使用AOP+Annotation实现操作日志记录

    先创建注解 OperInfo @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @ ...

  10. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

随机推荐

  1. ML—随机森林&#183;1

    Introduction to Random forest(Simplified) With increase in computational power, we can now choose al ...

  2. 安装mysql之后,存入中文出现乱码

    如图显示:安装mysql之后,存入中文出现乱码 解决方案: 找到如图的文件位置 打开进行如图的修改: 结果:

  3. Micosoft.ReportViewer.WebForms v 11.0... 1.0.1

    his dll is required for the use of Microsoft's forms based ReportViewer control. This version is to ...

  4. oracle 写入txt

    分几个步骤 1,创建输出路径,比如你要在/orcl/dir目录下输出,你就先要建立好这个路径,并在root用户下 chmod 777 /orcl/dir 2,sqlplus下以sysdba登录,执行以 ...

  5. C语言之浮点数

    #include<stdio.h> int main(){printf("请分别输入身高的英尺和英寸," "如输入\"5 7\"表示5英尺 ...

  6. Month format:number to English abbre

    ``` DATA LV_MONTH TYPE FCKTX. CLEAR:LV_MONTH,lv_date. SELECT SINGLE KTX INTO LV_MONTH FROM T247 WHER ...

  7. 初始ASP.NET数据控件GridView

    使用GridView控件绑定数据源 GridView控件个人认为就是数据表格控件,它以表格的形式显示数据源中的数据.每列表示一个字段,每行表示一条记录.     GridView控件支持在页面有一下功 ...

  8. lodop 二维码内容多少

    QRCode最多能放181个汉字:LODOP.ADD_PRINT_BARCODE(248,6,60,60,"QRCode","一二三四五六七八九十二二三四五六七八九十三二 ...

  9. python基础之赋值/深copy/浅copy

    首先,不管是赋值还是深浅copy,都是针对那些可能会产生变化的值进行区分的,也就是对于数字,字符串来说,区分赋值,深浅copy是毫无意义的. 那么,让我们来对那些可变的像list set dict t ...

  10. OpenERP 负载平衡

    OpenERP 7.0 带来了许多新特性,架构上也有许多改进.其中可配置 worker 参数,可使 OpenERP 运行在多进程模式,突破GIL的限制,有效利用了现代多核CPU的性能.但默认情况下,O ...