# @Log 注解使用指南 ## 一、概述 `@Log` 注解是 skills 项目中用于记录操作日志的核心功能。它基于 **AOP(面向切面编程)** 和 **拦截器** 机制实现,能够自动捕获 Controller 层方法的请求和响应信息,并异步保存到数据库的 `sys_log` 表中。 ## 二、核心特性 ✅ **无侵入性**: 只需添加 `@Log` 注解,无需修改业务代码 ✅ **自动化**: 请求/响应信息自动捕获,无需手动记录 ✅ **高性能**: 异步保存,不影响接口响应速度 ✅ **智能化**: 自动识别浏览器、操作系统、IP等信息 ✅ **可扩展**: 支持类级别和方法级别的注解配置 ## 三、快速开始 ### 3.1 数据库初始化 执行 SQL 脚本创建或更新日志表: ```bash # 在项目根目录执行 mysql -u root -p skills < backend/db/alter_sys_log_table.sql ``` ### 3.2 在控制器上添加注解 #### 方式一:类级别注解(推荐) 在 Controller 类上添加 `@Log` 注解,该类下所有方法都会记录日志: ```java @Log(module = "用户管理") @RestController @RequestMapping("/api/user") public class UserController { @PostMapping("/add") public CommonResult addUser(@RequestBody UserReq req) { // 自动记录日志 } } ``` #### 方式二:方法级别注解 在特定方法上添加 `@Log` 注解,只记录该方法的日志: ```java @RestController @RequestMapping("/api/order") public class OrderController { @Log(module = "订单管理", description = "创建订单") @PostMapping("/create") public CommonResult createOrder(@RequestBody OrderReq req) { // 自动记录日志 } } ``` #### 方式三:忽略特定接口 对于高频调用的接口,可以设置 `ignore = true` 不记录日志: ```java @Log(module = "认证") @RestController @RequestMapping("/api/auth") public class AuthController { @PostMapping("/login") public CommonResult login(@RequestBody LoginReq req) { // 会记录日志 } @Log(ignore = true) @GetMapping("/check/token") public CommonResult checkToken(@RequestParam String token) { // 不会记录日志(高频调用接口) } } ``` ## 四、注解属性说明 | 属性 | 类型 | 说明 | 默认值 | 示例 | |------|------|------|--------|------| | module | String | 模块名称 | 空字符串 | "用户管理" | | description | String | 日志描述 | 空字符串 | "创建用户" | | ignore | boolean | 是否忽略记录 | false | true/false | ## 五、已添加注解的控制器 以下控制器已经添加了 `@Log` 注解: 1. **LoginController** - 登录认证模块 - 路径: `/api/login/**` - 模块名: "登录认证" 2. **CmsContentController** - 内容管理模块 - 路径: `/api/cmsContent/**` - 模块名: "内容管理" 3. **AccountController** - 账户管理模块 - 路径: `/api/account/**` - 模块名: "账户管理" 4. **AccountFrozenController** - 账户冻结模块 - 路径: `/api/accountFrozen/**` - 模块名: "账户冻结" ## 六、日志记录内容 系统会自动记录以下信息: ### 6.1 请求信息 - 请求方法(GET、POST等) - 请求URL - 请求头 - 请求体 - 客户端IP - IP归属地 - 浏览器信息 - 操作系统 ### 6.2 响应信息 - 响应状态码 - 响应头 - 响应体(如果可用) - 执行耗时(毫秒) ### 6.3 其他信息 - 模块名称 - 日志描述 - 成功/失败状态 - 错误信息(如果失败) - 创建时间 ## 七、日志查询 ### 7.1 数据库查询 ```sql -- 查询最近的10条日志 SELECT * FROM sys_log ORDER BY create_time DESC LIMIT 10; -- 按模块查询 SELECT * FROM sys_log WHERE module = '登录认证' ORDER BY create_time DESC; -- 查询失败的日志 SELECT * FROM sys_log WHERE status = 2 ORDER BY create_time DESC; -- 按IP查询 SELECT * FROM sys_log WHERE ip = '192.168.1.100' ORDER BY create_time DESC; -- 按时间范围查询 SELECT * FROM sys_log WHERE create_time BETWEEN '2026-04-14 00:00:00' AND '2026-04-14 23:59:59' ORDER BY create_time DESC; ``` ### 7.2 通过 API 查询 可以使用现有的 SysLogController 提供的接口查询日志: ```bash # 分页查询 POST /api/sysLog/getPageList { "pageNum": 1, "pageSize": 10, "module": "登录认证", "status": 1 } ``` ## 八、性能优化 ### 8.1 异步保存 日志保存采用异步方式,不会阻塞主业务流程: ```java @Async public void saveLogAsync(LogRecord logRecord) { // 异步保存逻辑 } ``` ### 8.2 数据库索引 日志表已添加以下索引以优化查询性能: - `idx_module`: 模块查询优化 - `idx_ip`: IP查询优化 - `idx_create_time`: 时间范围查询优化 - `idx_status`: 状态查询优化 ### 8.3 定期清理 建议定期清理历史日志,避免数据量过大: ```sql -- 保留最近6个月的日志 DELETE FROM sys_log WHERE create_time < DATE_SUB(NOW(), INTERVAL 6 MONTH); ``` ## 九、常见问题 ### 9.1 日志未记录 **可能原因**: 1. 方法标注了 `@Log(ignore = true)` 2. 异步线程池配置问题 **排查步骤**: - 检查控制器是否添加了 `@Log` 注解 - 查看应用日志是否有错误信息 - 确认 `@EnableAsync` 已启用 ### 9.2 响应体为空 **原因**: 拦截器无法直接读取响应体 **解决方案**: - 当前版本暂时不记录响应体 - 如需记录,需要使用 ContentCachingResponseWrapper ### 9.3 用户ID为空 **原因**: 尚未实现从 Token 解析用户ID的逻辑 **解决方案**: - 在 LogInterceptor 的 `saveLogAsync` 方法中添加用户ID解析逻辑 - 根据实际使用的认证框架(如 Sa-Token)实现 ## 十、扩展开发 ### 10.1 添加用户ID解析 在 `LogInterceptor.saveLogAsync` 方法中添加: ```java // 从 Token 中解析用户ID try { String token = request.getHeaders().get("Authorization"); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); // 使用 Sa-Token 解析用户ID Object loginId = StpUtil.getLoginIdByToken(token); if (loginId != null) { sysLog.setCreateUser(Long.parseLong(loginId.toString())); } } } catch (Exception e) { logger.warn("解析用户ID失败: {}", e.getMessage()); } ``` ### 10.2 自定义日志过滤 可以在 `LogInterceptor.preHandle` 方法中添加自定义过滤逻辑: ```java // 排除敏感接口 String requestUri = request.getRequestURI(); if (requestUri.contains("/sensitive/")) { return true; // 不记录日志 } ``` ## 十一、技术架构 ### 11.1 核心组件 ``` ┌─────────────────────────────────────┐ │ Controller (@Log 注解) │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ LogInterceptor (拦截器) │ │ - preHandle: 捕获请求信息 │ │ - afterCompletion: 捕获响应信息 │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ LogInterceptor.saveLogAsync │ │ (@Async 异步保存) │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ SysLogMapper (持久层) │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ sys_log (MySQL 数据库表) │ └─────────────────────────────────────┘ ``` ### 11.2 相关文件 | 文件路径 | 说明 | |---------|------| | `annotation/Log.java` | @Log 注解定义 | | `entity/LogRecord.java` | 日志记录对象 | | `entity/SysLog.java` | 日志实体类 | | `interceptor/LogInterceptor.java` | 日志拦截器 | | `config/LogConfiguration.java` | 日志配置类 | | `mapper/SysLogMapper.java` | 日志 Mapper 接口 | | `resources/mapper/SysLogMapper.xml` | 日志 SQL 映射 | | `db/alter_sys_log_table.sql` | 数据库建表脚本 | ## 十二、总结 `@Log` 注解为 skills 项目提供了强大的操作日志记录功能,具有以下优势: - ✅ **简单易用**: 只需添加注解即可启用 - ✅ **性能优异**: 异步保存,不影响业务性能 - ✅ **信息完整**: 自动捕获请求/响应的详细信息 - ✅ **灵活配置**: 支持类级别和方法级别的配置 - ✅ **易于扩展**: 可以根据需求自定义日志记录逻辑 通过合理使用 `@Log` 注解,可以实现: - 操作审计追溯 - 安全事件监控 - 接口调用统计 - 问题排查分析 --- **文档版本**: v1.0 **最后更新**: 2026-04-14 **作者**: Lingma AI Assistant