• 基于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 emp日志.txt中获取到记录的日志内容。

    json格式的数据记录(参数JSON):
    {
    	"args": {
    		"id": 999,
    		"value": "content"
    	},
    	"clientIp": "192.168.1.54",
    	"content": "1. 请求来了,执行业务动作
    2. 业务动作执行完成
    ",
    	"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), 有时间会一直维护和优化。

  • 相关阅读:
    没有 Lambda 演算何来匿名函数——匿名函数(匿名方法和Lambda)、委托、LINQ
    HTML 4.01 符号实体
    利用 IHttpModule 自定义 HTTP 处理模块
    数据结构单链表
    Ext.Net 1.2.0_Ext.Net.RendererFormat 常用数据格式转换呈现格式
    ASP.NET 以 Request.Querystring、Request.Form 或 Request.Params 获取客户端的数据
    Flex>连接WebService
    java.sql.SQLException: 关闭的连接
    jsp>SmartUpload相关类说明
    flex>样式和主题
  • 原文地址:https://www.cnblogs.com/ealenxie/p/13208498.html
Copyright © 2020-2023  润新知