自定义注解实现日志记录
自定义注解实现日志记录
引入所需的依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>版本号</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.42</version>
</dependency>
</dependencies>
自定义注解(LogAnnotation)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
//模块标题
String title() default "";
//日志内容
String content() default "";
}
定义日志类
@Data
public class Log {
private String title;//模块标题
private String content; //日志内容
private String method; //方法名称
private String requestMethod; //请求方式
private String name; // 操作人员名称
private String requestUrl; //请求URL
private String requestIp; //请求IP
private String ipLocation;//ip归属
private String requestParam; //请求参数
private String responseParam; //响应参数
private Integer status; //操作状态 (0正常,1异常)
private String error; //错误信息
private Date executeTime; //执行时间
private Long takeTime; //该方法耗时(毫秒)
}
定义AOP切面类
用于拦截带有 @LogAnnotation
注解的方法,并记录日志
package com.chs.aspect;
import com.alibaba.fastjson.JSON;
import com.chs.annocation.LogAnnotation;
import com.chs.domain.Log;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(com.chs.annocation.LogAnnotation)")
public void logPointcut() {
}
//用于记录方法的执行时间
ThreadLocal<Long> startTime = new ThreadLocal<>();
@Before("logPointcut()")
public void beforeMethod() {
startTime.set(System.currentTimeMillis());
}
/**
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
*/
@Pointcut("execution(* com.chs.controller..*(..))")
public void allControllerPointCut() {
}
@AfterReturning("allControllerPointCut()")
public void saveLog(JoinPoint joinPoint) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
assert requestAttributes != null;
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
try {
//创建日志对象
Log log = new Log();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method m = methodSignature.getMethod();
// 获取方法上的注解对象
LogAnnotation annotation = m.getAnnotation(LogAnnotation.class);
// 获取值
String title = annotation.title();
log.setTitle(title);
String content = annotation.content();
log.setContent(content);
// 将入参转换成json
// String params = argsArrayToString(joinPoint.getArgs());
//设置请求参数
// log.setRequestParam(params);
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = m.getName();
methodName = className + "." + methodName + "()";
log.setMethod(methodName);
//获取请求的方法
String method = request.getMethod();
log.setName(method);
//获取请求方式
String requestMethod = request.getMethod();
log.setRequestMethod(requestMethod);
//获取操作人员名称
log.setName("张三");
//获取请求IP的归属地址
log.setIpLocation("陕西");
//获取请求URI
String requestURI = request.getRequestURI();
log.setRequestUrl(requestURI);
//获取IP地址
String Ip = this.getIp(request);
log.setRequestIp(Ip);
//获取返回结果
// String res = JSON.toJSONString(result);
// log.setResponseParam(res);
//设置操作状态
log.setStatus(0);
//设置执行时间
log.setExecuteTime(new Date());
//设置耗时时间
Long takeTime = System.currentTimeMillis() - startTime.get();
log.setTakeTime(takeTime);
System.out.println("log = " + log);
} catch (Throwable e) {
e.printStackTrace();
}
}
@AfterThrowing(pointcut = "allControllerPointCut()", throwing = "e")
public void saveErrorLog(JoinPoint joinPoint, Throwable e) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
assert requestAttributes != null;
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
try {
//创建日志对象
Log log = new Log();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method m = methodSignature.getMethod();
// 获取方法上的注解对象
LogAnnotation annotation = m.getAnnotation(LogAnnotation.class);
// 获取值
String title = annotation.title();
log.setTitle(title);
String content = annotation.content();
log.setContent(content);
// 将入参转换成json
// String params = argsArrayToString(joinPoint.getArgs())
//设置请求参数
// log.setRequestParam(params);
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = m.getName();
methodName = className + "." + methodName + "()";
log.setMethod(methodName);
//获取请求的方法
String method = request.getMethod();
log.setName(method);
//获取请求方式
String requestMethod = request.getMethod();
log.setRequestMethod(requestMethod);
//获取操作人员名称
log.setName("张三");
//获取请求IP的归属地址
log.setIpLocation("陕西");
//获取请求URI
String requestURI = request.getRequestURI();
log.setRequestUrl(requestURI);
//获取IP地址
String Ip = this.getIp(request);
log.setRequestIp(Ip);
//获取返回结果
// String res = JSON.toJSONString(result);
// log.setResponseParam(res);
//设置操作状态
log.setStatus(0);
//设置执行时间
log.setExecuteTime(new Date());
//设置耗时时间
Long takeTime = System.currentTimeMillis() - startTime.get();
log.setTakeTime(takeTime);
//设置错误信息
log.setError(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
System.out.println("log = " + log);
} catch (Throwable e2) {
e2.printStackTrace();
}
}
//转换异常信息为字符串
private String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
message = substring(message, 0, 2000);
return message;
}
//字符串截取
public static String substring(String str, int start, int end) {
if (str == null) {
return null;
} else {
if (end < 0) {
end += str.length();
}
if (start < 0) {
start += str.length();
}
if (end > str.length()) {
end = str.length();
}
if (start > end) {
return "";
} else {
if (start < 0) {
start = 0;
}
if (end < 0) {
end = 0;
}
return str.substring(start, end);
}
}
}
//根据HttpServletRequest获取访问者的IP地址
private String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
使用自定义注解的控制器
@LogAnnotation(title = "用户模块", content = "删除用户操作")
@GetMapping("/delete/{userId}")
public String delete(@PathVariable("userId") Long userId){
System.out.println("userId = " + userId);
int i=10/0; //测试异常日志
return "成功";
}
注意:记得在主启动类上加:
@EnableAspectJAutoProxy
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix