【任务14】日志系统:设计一个简单的日志系统

本问题对应的项目链接:从0设计一个web项目
具体打卡要求:
1、表的名字为 record_log,请给出你的表的创建语句(后面需要提供搜索功能,所以有些字段记录加索引)
2、简单说一下你的实现思路

另外,市面上有挺多日志组件,你们感兴趣要看也行,我随便找了一个GitHub:https://github.com/mouzt/mzt-biz-log/

共以下 1 个回答

  • 秊 永久会员 2022年10月16日 下午9:39

    [10. 17] 遇到的问题: 在对登录(/Login)接口 和 注册(/Register)接口进行日志记录时,无法准确获得用户的账号信息(op_ac)。

    原因为:目前我是在用户访问 /Login 接口时,在用户本地增加一个 Cookie 来记录用户的账号(使用HttpServletResponse.addCookie( ) 方法),但用户访问 /Login 接口时的 HttpServletRequest 不能获得也是在这个接口中添加的 Cookie 信息,即:虽然这个接口的方法中添加了 Cookie,但此时还未同步到 requset 中(大致是这个意思),必须要这个方法完全结束后的下一次 HttpServletRequest 请求中才能生效。

    解决:

    我尝试了在日志系统的环绕通知中先执行 Object result = joinPoint.proceed(); 再获取 HttpServletRequest,不行。
    使用 @AfterReturning,返回后通知,也不行。
    最后我又想可能是这两个接口对于【通用日志记录系统】来说比较特殊,需要单独为他俩写一个日志记录处理方法,然后在这两个方法的返回结果中记录用户的账号信息,这样就能为这两个接口准确记录日志了。目前使用的是这种方法🤪

    测试:

    建表语句:

    drop table if exists record_log;
    create table record_log (
    op_time bigint not null,
    op_ac varchar(50) default(NULL),
    status varchar(50) not null default(NULL),
    op_type varchar(50) not null default(NULL),
    req_url varchar(50) not null default(NULL),
    req_mtd varchar(255) not null default(NULL),
    op_desc varchar(50) default(NULL),
    err_msg varchar(50) default(NULL),
    time_csm varchar(50) not null default(NULL),
    op_ip varchar(50) not null default(NULL),
    primary key (op_time)
    ) engine=innodb default charset=utf8mb4 comment='系统操作日志';

    ALTER TABLE record_log ADD INDEX idx_ac (op_ac(12), status, op_type);

    字段 说明
    op_time 操作时间
    op_type 操作类型
    op_ac 操作用户ID
    op_ip 操作用户IP
    op_desc 操作描述
    status 是否成功
    err_msg 异常信息
    time_csm 耗时
    req_url 请求地址
    req_mtd 类名方法

    思路: 基于AOP实现:设计一个需要记录日志的注解类(@LogAnnotation),则切点即为带有此注解的接口,使用环绕通知的方式进行日志打印,同时将日志记录到 mysql 中(记录操作使用异步线程完成)。
    部分实现代码如下:

    // annotation/LogAnnotation
    @Documented
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LogAnnotation {
    
    // 操作类型
    String opType() default "";
    
    // 操作说明
    String opDesc() default "";
    
    }
    
    // aspect/LogAspect
        @Pointcut("@annotation(com.jiang.fzte.annotation.LogAnnotation)")
            public void pointCut() {}
    
    @Around("pointCut()")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long opTime = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
    
    // 执行方法
    Object result = joinPoint.proceed();
    
    // 保存日志
    saveLog(request, joinPoint, opTime, Long.toString(System.currentTimeMillis() - opTime));
    
    return result;
    
    }
    

    分别在用户登录和用户注册接口启用@LogAnnotation注解,测试结果如

    Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev

    4 赞同 0 条回复

# 回答此问题

后才能回答