终于不用再问运维要日志文件了

举报
一只牛博 发表于 2024/12/27 15:14:19 2024/12/27
【摘要】 有话说不知道大家是否和我一样每次去接第三方数据,或者说推数据给第三方的时候都会打印日志,随后屁颠屁颠的去找运维要日志文件,看是否推送成功。应该有不少人是这样的,日志打印都放到某一个日志文件夹下,每次找日志都很麻烦存在的问题:会有很多日志文件产生(实时性很强的或者说数据大的)不容易定位数据如果日志文件你可以查看(有权限)还好,如果你不能查看,每次都要去麻烦别人不好分类或者发现问题(第三方很多...

有话说

不知道大家是否和我一样每次去接第三方数据,或者说推数据给第三方的时候都会打印日志,随后屁颠屁颠的去找运维要日志文件,看是否推送成功。

应该有不少人是这样的,日志打印都放到某一个日志文件夹下,每次找日志都很麻烦

存在的问题:

  1. 会有很多日志文件产生(实时性很强的或者说数据大的)
  2. 不容易定位数据
  3. 如果日志文件你可以查看(有权限)还好,如果你不能查看,每次都要去麻烦别人
  4. 不好分类或者发现问题(第三方很多,数据偶尔会断掉)

如果你所在公司比较厉害,可能使用如下方法

  1. 使用数据库来存储(单表数据会很大)
  2. 专用的日志系统 从零开始,用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合

还有很多比较牛X的方法大家可以推荐一哈

构思

基础逻辑如下

为什么使用@after通知

在 AOP(Aspect-Oriented Programming)中,afterafterReturning 是两种常见的后置通知,它们的执行时机有所不同:


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);
    }
    
    • 只有方法正常返回时才会打印结果日志。

谁更“后”一点?

  • afterafterReturning 更后

    ,因为:

    • afterReturning 只会在方法正常返回后执行;
    • after 无论方法是正常返回还是抛出异常,都会在方法执行完成的最后时刻触发。

执行时机对比

以下是执行流程示例:

方法执行的四种阶段:
  1. 前置通知@Before

  2. 目标方法执行

  3. 后置通知

    • 如果是正常返回:@AfterReturning -> @After
    • 如果是异常:@AfterThrowing -> @After
对比:
通知类型 执行条件 触发时机
@After 无论方法是否抛异常 方法执行结束后
@AfterReturning 方法正常返回时才触发 方法执行结束后

示例说明

假设目标方法如下:

public String testMethod() {
    return "success";
}
  1. 如果执行正常:
    • 执行顺序@Before -> 方法体 -> @AfterReturning -> @After
  2. 如果方法抛出异常:
    • 执行顺序@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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。