终于不用再问运维要日志文件了
【摘要】 有话说不知道大家是否和我一样每次去接第三方数据,或者说推数据给第三方的时候都会打印日志,随后屁颠屁颠的去找运维要日志文件,看是否推送成功。应该有不少人是这样的,日志打印都放到某一个日志文件夹下,每次找日志都很麻烦存在的问题:会有很多日志文件产生(实时性很强的或者说数据大的)不容易定位数据如果日志文件你可以查看(有权限)还好,如果你不能查看,每次都要去麻烦别人不好分类或者发现问题(第三方很多...
有话说
不知道大家是否和我一样每次去接第三方数据,或者说推数据给第三方的时候都会打印日志,随后屁颠屁颠的去找运维要日志文件,看是否推送成功。
应该有不少人是这样的,日志打印都放到某一个日志文件夹下,每次找日志都很麻烦
存在的问题:
- 会有很多日志文件产生(实时性很强的或者说数据大的)
- 不容易定位数据
- 如果日志文件你可以查看(有权限)还好,如果你不能查看,每次都要去麻烦别人
- 不好分类或者发现问题(第三方很多,数据偶尔会断掉)
如果你所在公司比较厉害,可能使用如下方法
- 使用数据库来存储(单表数据会很大)
- 专用的日志系统 从零开始,用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合
还有很多比较牛X的方法大家可以推荐一哈
构思
基础逻辑如下
为什么使用@after通知
在 AOP(Aspect-Oriented Programming)中,after
和 afterReturning
是两种常见的后置通知,它们的执行时机有所不同:
after
(后置通知)
-
执行时机:方法执行 结束后 无论结果如何(正常返回、抛出异常等),都会执行。
-
适合用于清理资源或记录日志等不依赖方法结果的操作。
-
示例
:
@After("execution(* com.example.service.*.*(..))") public void afterAdvice() { System.out.println("After method execution"); }
- 不管方法是成功返回还是抛出了异常,都会打印日志。
afterReturning
(返回通知)
-
执行时机:方法执行 成功返回结果后 才会执行(即未抛出异常时)。
-
适合用于需要处理方法返回值的场景。
-
示例
:
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result") public void afterReturningAdvice(Object result) { System.out.println("Method returned with result: " + result); }
- 只有方法正常返回时才会打印结果日志。
谁更“后”一点?
-
after
比afterReturning
更后,因为:
afterReturning
只会在方法正常返回后执行;after
无论方法是正常返回还是抛出异常,都会在方法执行完成的最后时刻触发。
执行时机对比
以下是执行流程示例:
方法执行的四种阶段:
-
前置通知:
@Before
-
目标方法执行
-
后置通知
:
- 如果是正常返回:
@AfterReturning
->@After
- 如果是异常:
@AfterThrowing
->@After
- 如果是正常返回:
对比:
通知类型 | 执行条件 | 触发时机 |
---|---|---|
@After |
无论方法是否抛异常 | 方法执行结束后 |
@AfterReturning |
方法正常返回时才触发 | 方法执行结束后 |
示例说明
假设目标方法如下:
public String testMethod() {
return "success";
}
- 如果执行正常:
- 执行顺序:
@Before
-> 方法体 ->@AfterReturning
->@After
- 执行顺序:
- 如果方法抛出异常:
- 执行顺序:
@Before
-> 方法体 ->@AfterThrowing
->@After
- 执行顺序:
总结
@After
的触发范围更广,始终是最后一个执行的通知。@AfterReturning
只在方法正常返回时触发,发生在@After
之前。
为什么使用先创建表
如果我们采用的是时间纬度的分表,可以创建12个表对应的12个月,如果采用实时创建的话对于创建时机可能要仔细定夺,所以还不如直接给它创建完
注解实现
package com.todoitbo.baseSpringbootDasmart.annotation;
import java.lang.annotation.*;
/**
* 自定义操作日志注解
*
* @author bo
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 功能模块
*
* @return java.lang.String
*/
String operModule() default "";
/**
* 请求类型
*
* @return java.lang.String
*/
String operType() default "";
/**
* description: describe
* @return java.lang.String
* @since 2024/11/22
*/
String describe() default "";
}
AOP实现
/**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
*/
@Pointcut("@annotation(com.todoitbo.baseSpringbootDasmart.annotation.SysLog)")
public void operLogPointCut() {
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
*
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operLogPointCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
// 获取用户信息
SysLoginInfo sysLoginInfo = LoginContextUtil.getSysLoginInfo();
SysOperationLog sysOperationLog = new SysOperationLog();
try {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作
SysLog opLog = method.getAnnotation(SysLog.class);
if (opLog != null) {
sysOperationLog.setOpDescribe(opLog.describe());
sysOperationLog.setModule(opLog.operModule());
}
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
sysOperationLog.setId(IdUtil.getSnowflake().nextId());
sysOperationLog.setMethod(methodName);
sysOperationLog.setRequest(JSONObject.toJSONString(joinPoint.getArgs()));
sysOperationLog.setResponse(JSONObject.toJSONString(keys));
sysOperationLog.setUrl(request.getRequestURI());
sysOperationLog.setIp(IpUtil.getIpAddr(request));
sysOperationLog.setUserId(ObjectUtil.isNotEmpty(sysLoginInfo) ? sysLoginInfo.getSysUser().getId() : 0);
sysOperationLog.setUserName(ObjectUtil.isNotEmpty(sysLoginInfo) ? sysLoginInfo.getSysUser().getUserName() : "无登录信息!");
sysOperationLogService.insertSysOperationLog(sysOperationLog);
} catch (Exception e) {
throw new BusinessException("保存操作日志失败!");
}
}
效果展示
依次调用post和get方法
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)