基于AOP和ThreadLocal实现日志记录
基于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实现日志记录的更多相关文章
- 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块
Log4a 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块 github地址 : https://github.com/EalenXie/log4a 在API每次被请求时 ...
- 来一手 AOP 注解方式进行日志记录
系统日志对于定位/排查问题的重要性不言而喻,相信许多开发和运维都深有体会. 通过日志追踪代码运行状况,模拟系统执行情况,并迅速定位代码/部署环境问题. 系统日志同样也是数据统计/建模的重要依据,通过分 ...
- 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil
封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...
- 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,nloglogutil
封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil,代码比较简单,主要是把MongoTarget的配置.FileTarget的配置集成到类中,同时利用缓存依赖来判断是否需要重新创 ...
- Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!
Go/Python/Erlang编程语言对比分析及示例 本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...
- 利用AOP与ToStringBuilder简化日志记录
刚学spring的时候书上就强调spring的核心就是ioc和aop blablabla...... IOC到处都能看到...AOP么刚开始接触的时候使用在声明式事务上面..当时书上还提到一个用到ao ...
- springboot整合aop实现网站访问日志记录
目的: 统一日志输出格式,统计访问网站的ip. 思路: 1.针对不同的调用场景定义不同的注解,目前想的是接口层和服务层. 2.我设想的接口层和服务层的区别在于: (1)接口层可以打印客户端IP,而服务 ...
- spring aop通过注解实现日志记录
首先是几个概念:连接点(Joinpoint).切点(Pointcut).增强(Advice).切面(Aspect) 另外也要使用到注解. 需求:通过注解定义LogEnable.然后程序运行能够识别定义 ...
- 使用AOP+Annotation实现操作日志记录
先创建注解 OperInfo @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @ ...
随机推荐
- Spring Boot笔记(六) springboot 集成 timer 定时任务
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.创建具体要执行的任务类: package com.example.poiutis.timer; im ...
- Java实现 LeetCode 214 最短回文串
214. 最短回文串 给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出 ...
- Java实现 LeetCode 33 搜索旋转排序数组
33. 搜索旋转排序数组 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值, ...
- tcpdump 基于mac地址抓取数据包
1.刚刚接触tcpdump时,常用tcpdump -i eth1 host 192.168.1.1 这个命令基于ip地址抓取数据包信息. tcpdump -i eth1(接口名称) host 192. ...
- 玩转计划任务命令:schtasks
管理计划任务SCHTASKS /parameter [arguments] 描述: 允许管理员创建.删除.查询.更改.运行和中止本地或远程系统上的计划任务. 参数列表: /Create ...
- 【asp.net core】7 实战之 数据访问层定义
0. 前言 在上一篇,我们搭建了一个项目框架,基本上是一个完整的项目.目前而言,大部分的应用基本都是这个结构.好的,不废话了,进入今天的议题:完成并实现数据层的基础实现. 1. 数据实体 通常情况下, ...
- tensorflow2.0学习笔记第二章第四节
2.4损失函数损失函数(loss):预测值(y)与已知答案(y_)的差距 nn优化目标:loss最小->-mse -自定义 -ce(cross entropy)均方误差mse:MSE(y_,y) ...
- Redis之分布式锁实现
点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...
- 轻松解决Github连接缓慢、图裂问题
1 简介 gayhub(误)github作为全世界最大的开源代码库以及版本控制系统,是用来托管项目以及学习开源技术非常好的平台,是我心中最好的学习网站,我们公众号的众多技术文章对应的数据和代码也都一直 ...
- 使用 UniApp 实现小程序的微信登录
微信登录思路: 在main.js 中封装公共函数,用于判断用户是否登录 在main.js 中分定义全局变量,用于存储接口地址 如果没有登录.则跳转至登录页面 进入登录页面 通过 wx.login 获取 ...