SpringBoot 日志记录(面向切面)
引入依赖
<!--利用 AOP 做操作日志记录-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebLog {
/**
* 渠道
* @return 渠道标识
*/
String channel() default "web";
/**
* 功能名称
* @return 功能名称
*/
String name() default "";
/**
* 方法名称
* @return 方法名称
*/
String action() default "";
/**
* 是否保存(默认不保存)
* @return 是否保存
*/
boolean saveFlag() default false;
}
@Aspect // 表明此类为一个切面
@Component // 随着框架的启动而启动
public class WebLogAspect {/**
* 获取日志类,方便直接在控制台输出统一格式的日志信息
*/
private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
/**
* 以 controller 包下定义的所有请求为切入点
*/
@Pointcut("execution(public * net.parallel.controller.*.*(..))" +
"||execution(public * net.parallel.feign.*.*(..))")
public void webLog() {
}
/**
* 在切点之前织入
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()") // webLog():是你@Pointcut注解的方法名
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
if (BeanUtil.isEmpty(request) || StringUtils.isBlank(request.getRequestURL())) {
return;
}
// 打印请求相关参数
logger.info("========================================== Start ==========================================");
// 打印请求 url
logger.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
logger.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
logger.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
logger.info("Request Args : {}", new Gson().toJson(joinPoint.getArgs()));
// 获取 @ApiOperatjoinPoint = {MethodInvocationProceedingJoinPoint@21287} "execution(RestResponse net.evecom.parallel.controller.ParallelWorkController.businessAddParallelWork(String,String,String,String,String,String,String,String,String,String,String,String,String))"ion("") 注解中的信息,此注解属于swagger的,但也能获取,其他注解可举一反三
ApiOperation apiOperation = null;
// JoinPoint:oinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.大概就是获取目标方法的一些信息
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
apiOperation = ms.getMethod().getDeclaredAnnotation(ApiOperation.class);
if (apiOperation != null) { ;
logger.info("操作 : {}", apiOperation.value());
}
}
/**
* 在切点之后织入
*
* @throws Throwable
*/
@After("webLog()")
public void doAfter() throws Throwable {
logger.info("=========================================== End ===========================================");
// 每个请求之间空一行
logger.info("");
}
/**
* 环绕
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = pjp.proceed();
// 打印出参
logger.info("Response Args : {}", new Gson().toJson(result));
// 执行耗时
logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
Object[] paramValues = pjp.getArgs();//获取参数数组
Signature sig = pjp.getSignature();//获取方法对象
MethodSignature msig = (MethodSignature )sig;
Method method = msig.getMethod();
//获取自定义注解的属性值
WebLog ann = method.getAnnotation(WebLog.class);
if (BeanUtil.isEmpty(ann)) {
return result;
}
logger.info("请求参数: {}", JSON.toJSONString(paramValues));
logger.info("返回值:{}", new Gson().toJson(result));return result;
}
}
controller层
/**
* 新增联办件调用接口(营业执照)—— 变更/注销
* @return
*/
@ApiOperation("新增联办件调用接口")
@PostMapping("businessAddParallelWork")
@WebLog(channel = "web", name = "新增联办件调用接口", action = "parallelWork/businessAddParallelWork", saveFlag = true)
public RestResponse businessAddParallelWork(@RequestBody ParallelWorkOrderActvEntity parallelWorkOrderActvEntity) {
// 业务代码...
return null;
}
Feign接口调用
@FeignClient(name = "license-service", contextId = "license-service-approve")
public interface LicServiceFeign {
/**
* 变更流程发起
*
* @param json
* @return
*/
@WebLog(channel = "feign", name = "变更流程发起", action = "/lic/lic-change/saveTempData", saveFlag = true)
@PostMapping("/lic/lic-change/saveTempData")
RestResponse licChangeFlowStart(@RequestBody JSONObject json, @RequestHeader("Authorization") String authorization);
/**
* 注销流程发起
*
* @param json
* @return
*/
@WebLog(channel = "feign", name = "注销流程发起", action = "/lic/lic-cancel/save", saveFlag = true)
@PostMapping("/lic/lic-cancel/save")
RestResponse licCancelFlowStart(@RequestBody JSONObject json, @RequestHeader("Authorization") String authorization);
}
__EOF__

本文作者:橘子味儿的猫
本文链接:https://www.cnblogs.com/zyulike/p/17669077.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/zyulike/p/17669077.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构