系统日志功能实现
# 系统日志功能实现
在企业级应用中,记录系统日志是保障系统安全性、审计操作行为、定位问题的重要手段。通过记录用户操作的内容,我们可以追溯系统中的关键业务活动。若依框架提供了使用注解的方式来简化日志记录,只需在 Controller 方法上添加 @Log
注解,即可自动记录操作日志。
# 一、@Log
注解参数说明
若依框架中的 @Log
注解用于标注需要记录日志的 Controller 方法,开发者可以通过设置注解参数来自定义日志的记录内容和方式。以下是 @Log
注解的详细参数说明:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
title | String | 空 | 操作模块的名称,例如“用户管理” |
businessType | BusinessType | OTHER | 操作功能类型(如:新增、修改、删除等) |
operatorType | OperatorType | MANAGE | 操作人类别(如:后台用户、手机端用户等) |
isSaveRequestData | boolean | true | 是否保存请求的参数 |
isSaveResponseData | boolean | true | 是否保存响应的参数 |
excludeParamNames | String[] | {} | 排除指定的请求参数,不记录这些参数到日志中 |
# 1. title
参数
title
参数用于指定操作模块的名称,通常是业务模块的名称。例如,在“用户管理”模块中新增用户操作时,可以将 title
设置为“用户管理”。
@Log(title = "用户管理", businessType = BusinessType.INSERT)
public AjaxResult addSave(User user)
{
return success(userService.insertUser(user));
}
2
3
4
5
# 2. businessType
参数
businessType
参数用于指定操作的功能类型,若依框架内置了多种常见的业务操作类型,如新增(INSERT)、修改(UPDATE)、删除(DELETE)等。开发者也可以自定义业务操作类型。
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
public AjaxResult editSave(User user)
{
return success(userService.updateUser(user));
}
2
3
4
5
# 3. operatorType
参数
operatorType
参数用于指定操作人的类别,常见的类别有后台用户(MANAGE)和手机端用户(MOBILE)。在不同的场景下,可以根据需要设置不同的操作人类别。
@Log(title = "用户管理", businessType = BusinessType.DELETE, operatorType = OperatorType.MANAGE)
public AjaxResult remove(Long userId)
{
return success(userService.deleteUserById(userId));
}
2
3
4
5
# 4. isSaveRequestData
参数
isSaveRequestData
参数用于控制是否保存请求的参数。默认情况下,若依会自动记录请求参数,但在某些场景下(如涉及敏感信息)可以选择不保存。
@Log(title = "用户管理", businessType = BusinessType.INSERT, isSaveRequestData = false)
public AjaxResult addSave(User user)
{
return success(userService.insertUser(user));
}
2
3
4
5
# 5. isSaveResponseData
参数
isSaveResponseData
参数用于控制是否保存响应的数据。默认情况下,若依会自动记录响应数据,如果不需要记录响应数据,可以将此参数设置为 false
。
@Log(title = "用户管理", businessType = BusinessType.INSERT, isSaveResponseData = false)
public AjaxResult addSave(User user)
{
return success(userService.insertUser(user));
}
2
3
4
5
# 6. excludeParamNames
参数
excludeParamNames
参数用于指定不记录的请求参数,避免将某些敏感信息记录到日志中。
@Log(title = "用户管理", businessType = BusinessType.INSERT, excludeParamNames = {"password", "salt"})
public AjaxResult addSave(User user)
{
return success(userService.insertUser(user));
}
2
3
4
5
# 二、自定义操作功能类型
若依框架允许开发者根据业务需求自定义操作功能类型。以下是如何添加自定义业务操作类型的步骤:
# 1. 在 BusinessType
枚举中新增业务操作类型
首先,打开 BusinessType
枚举类,添加新的操作类型。例如,添加一个用于测试的操作类型 TEST
。
package com.ruoyi.common.enums;
/**
* 业务操作类型
*
* @author ruoyi
*/
public enum BusinessType
{
// 其他操作类型省略...
/**
* 测试操作
*/
TEST,
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2. 在数据库字典表中初始化操作业务类型
接着,在 sys_dict_data
字典表中初始化操作业务类型,以便在日志管理中正确显示自定义操作类型。
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark)
VALUES (100, 10, '测试', '10', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', NOW(), 'admin', NOW(), '测试操作');
2
# 3. 在 Controller 中使用自定义的操作类型
在需要记录日志的 Controller 方法中使用自定义的 BusinessType
。
@Log(title = "测试模块", businessType = BusinessType.TEST)
public AjaxResult testOperation(User user)
{
return success(userService.testOperation(user));
}
2
3
4
5
# 三、操作日志记录逻辑实现
操作日志的记录逻辑通常通过 AOP(面向切面编程)来实现,若依框架通过 LogAspect
切面类来拦截标记了 @Log
注解的方法,并将操作日志记录到数据库中。
# 1. LogAspect
切面类的基本实现
LogAspect
是日志记录的切面类,负责拦截所有标记了 @Log
注解的方法,并根据注解参数执行日志记录操作。
package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect
{
// 定义切点,拦截所有标记了@Log注解的方法
@Pointcut("@annotation(com.ruoyi.common.annotation.Log)")
public void logPointCut() {}
// 前置通知,执行方法前记录日志
@Before("logPointCut()")
public void doBefore(JoinPoint joinPoint)
{
handleLog(joinPoint, null);
}
// 异常通知,记录异常信息
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e)
{
handleLog(joinPoint, e);
}
// 日志处理逻辑
protected void handleLog(final JoinPoint joinPoint, final Exception e)
{
try
{
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// 获取当前用户信息
String username = SecurityUtils.getUsername();
// 获取请求方法、参数等信息
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
// 异步保存日志
AsyncManager.me().execute(AsyncFactory.recordOperLog(username, className, methodName, controllerLog, e));
}
catch (Exception exp)
{
// 记录日志异常
}
}
// 获取注解
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (method != null)
{
return method.getAnnotation(Log.class);
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 2. 操作日志的异步保存
为了避免影响业务操作的性能,若依框架将日志记录的操作放到异步任务中执行。AsyncFactory
工厂类用于创建异步任务,AsyncManager
管理这些任务的执行。
package com.ruoyi.framework.manager.factory;
import com.ruoyi.system.domain.SysOperLog;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncFactory
{
@Async
public static void recordOperLog(String username, String className, String methodName, Log log, Exception e)
{
SysOperLog operLog = new SysOperLog();
operLog.setOperName(username
);
operLog.setOperMethod(className + "." + methodName);
operLog.setBusinessType(log.businessType().ordinal());
operLog.setStatus(e == null ? BusinessStatus.SUCCESS.ordinal() : BusinessStatus.FAIL.ordinal());
// 异步保存日志到数据库
SpringUtils.getBean(ISysOperLogService.class).insertOperLog(operLog);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 四、系统日志查询
若依框架提供了完善的系统日志查询功能,管理员可以通过“系统管理-操作日志”模块查看所有操作日志的详细信息,包括操作时间、操作人、操作类型、请求参数、响应结果等。
# 1. 日志查询接口
系统日志查询的后端接口通常位于 SysOperLogController
控制器中,支持分页查询、日志详情查看等功能。
@RestController
@RequestMapping("/system/operlog")
public class SysOperLogController extends BaseController
{
@Autowired
private ISysOperLogService operLogService;
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog)
{
startPage();
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
return getDataTable(list);
}
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable Long[] operIds)
{
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
@GetMapping("/detail/{operId}")
public AjaxResult detail(@PathVariable Long operId)
{
return success(operLogService.selectOperLogById(operId));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 五、总结
通过 @Log
注解和 AOP 技术,若依框架简化了系统日志的记录过程,使开发者能够专注于业务逻辑而无需担心日志的记录细节。
- 日志记录: 通过
@Log
注解,轻松记录操作日志,支持自定义操作类型、操作人类别、请求和响应数据等。 - 异步保存: 通过异步任务的方式保存日志,确保日志记录不会影响业务操作的性能。
- 日志查询: 提供完善的日志查询功能,支持分页查询、日志详情查看等,方便管理员追踪系统操作记录。