栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用SpEL记录操作日志的详细信息

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用SpEL记录操作日志的详细信息

操作日志

操作日志就是记录用户请求了什么接口,干了啥事儿。常见且简单的实现就是通过spring的aop + 自定义注解完成。

在HandlerMethod方法上标识自定义注解,在注解上设置一些自定义的基本属性,例如字符串属性 operation: “删除了用户信息”。
在Aop切面中,获取到这个注解,读取到预定义的信息,配合当前用户的Token,那么就确定了执行操作的用户和执行的操作。就可以创建一条“操作日志”。

不够详细

这种方式,也有一个缺点显而易见,就是日志的内容,被固定住了。我们只能知道“谁删除了用户”,但是不知道删除了哪些用户。
“被删除用户”的请求信息包含在了请求体或者是查询参数中,在Aop切面中,可以获取到request/response/Handler的参数等对象
但是不同的业务接口,参数一般都不相同,这就导致同一个日记录器不能公用。为每一个接口,写一个日志记录Aop?这很显然不是一个很好的办法。

SpEL

SpEL(Spring expression Language),Spring表达式语言。通俗的理解,就是可以通过一些字符串的表达式,完成一些“编程”的功能。
例如,读取/设置某些对象的属性。

有了这个东西后,我们就可以在日志注解中,设置一些字符串形式“表达式”,通过表达式来完成对某些属性(body/header/param)的读取。
这就非常地灵活,不同的接口,表达式不同,就可以读取到不同的信息,生成不同的日志。

学习SpEL详情可以查看官方文档,这里不会涉及太多。

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions

演示 OperationLog

自定义的日志注解

package io.springcloud.web.log;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;



@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
	
	
	String expression();
}

OperationLogAop

日志的Aop实现

package io.springcloud.web.log;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.expression.evaluationContext;
import org.springframework.expression.expressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelexpressionParser;
import org.springframework.expression.spel.support.StandardevaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import lombok.extern.slf4j.Slf4j;

@Aspect  
@Component 
@Order(-1)
@Slf4j
public class OperationLogAop {

	// 需要被SpEl解析的模板前缀和后缀 {{ expression  }}
	public static final TemplateParserContext TEMPLATE_PARSER_ConTEXT = new TemplateParserContext("{{", "}}");

	// SpEL解析器
	public static final expressionParser expression_PARSER = new SpelexpressionParser();
	
	
	@Pointcut(value="@annotation(io.springcloud.web.log.OperationLog)")
	public void controller() {};
	
	@Before(value = "controller()")
	public void actionLog (JoinPoint joinPoint) throws Throwable {
		
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();

		// 参数
		Object[] args = joinPoint.getArgs();
		
		// 参数名称
		String[] parameterNames = signature.getParameterNames();

		// 目标方法
		Method targetMethod = signature.getMethod();

		// 方法上的日志注解
		OperationLog operationLog = targetMethod.getAnnotation(OperationLog.class);

		// request
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

		// response
		// HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
		
		try {
			
	    	evaluationContext evaluationContext = new StandardevaluationContext();
			for (int i = 0; i < args.length; i ++) {
				evaluationContext.setVariable(parameterNames[i], args[i]);
			}
			
	    	String logContent = expression_PARSER.parseexpression(operationLog.expression(), TEMPLATE_PARSER_CONTEXT).getValue(evaluationContext, String.class);
	    	
			// TODO 异步存储日志
			
			log.info("operationLog={}", logContent);
		} catch (Exception e) {
			log.error("操作日志SpEL表达式解析异常: {}", e.getMessage());
		}
	}
}
Controller
package io.springcloud.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Data;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.springcloud.web.log.OperationLog;

import java.util.List;

@Data
class Payload {
	@NotNull
	private Integer id;
	@NotBlank
	private String userName;
	@Size(min = 1, max = 5)
	private List hobby;
}

@RestController
@RequestMapping("/test")
@Validated
public class TestController {

    
	@PostMapping
	@OperationLog(expression = "更新了用户"
			+ ", userAgent = {{ #request.getHeader('User-Agent') }}"
			+ ", action = {{ #action }}"
			+ ", id = {{ #payload.id }}"
			+ ", userName = {{ #payload.userName }}"
			+ ", hobbyLength = {{ #payload.hobby.size() }}")
	public Object test (HttpServletRequest request,
						HttpServletResponse response,
						@RequestParam("action") String action,
						@RequestBody @Validated Payload payload) {
		return ResponseEntity.ok(payload);
	}
}
测试 Request/Response
POST /test?action=update HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.28.4
Accept: */*
Postman-Token: c9564e75-03ad-42f0-8fea-5e34912bcfb8
Host: localhost
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 80
 
{
"id": "1",
"userName": "cxk",
"hobby": ["唱", "跳", "Rap"]
}
 
HTTP/1.1 200 OK
X-Response-Time: 2
Connection: keep-alive
Server: PHP/7.3.1
X-Request-Id: 553b4c5d-5363-4e53-9978-a0671e9aa16d
Content-Type: application/json;charset=UTF-8
Content-Length: 84
Date: Tue, 19 Oct 2021 04:28:46 GMT
 
{
"id": 1,
"userName": "cxk",
"hobby": [
"唱",
"跳",
"Rap"
]
}
日志

成功读取到了对应的属性

2021-10-19 12:28:46.831  INFO 2692 --- [  XNIO-1 task-1] io.springcloud.web.log.OperationLogAop   : operationLog=更新了用户, userAgent = PostmanRuntime/7.28.4, action = update, id = 1, userName = cxk, hobbyLength = 3

首发:https://springboot.io/t/topic/4248

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/337021.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号