思路总结:
需要在日志记录的方法中添加一个自定义注解,再去实现一个日志AOP类,AOP类把自定义注解设置为切点,当系统执行某一个添加了自定义注解的方法时,AOP会自动获取该方法名称以及用户信息实现日志记录。
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<!-- druid数据源驱动 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- UserAgent -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 自定义注解
定义一个方法级别的@Log注解,用于标注需要监控的方法:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
3. 创建库表
在数据库中创建一张sys_log表,用于保存用户的操作日志
-- ----------------------------
-- Table structure for sys_log
-- ----------------------------
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log` (
`id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`operation` varchar(255) DEFAULT NULL,
`time` decimal(10,0) DEFAULT NULL,
`method` varchar(255) DEFAULT NULL,
`params` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`ip` varchar(255) DEFAULT NULL,
`browser` varchar(255) DEFAULT NULL,
`system` varchar(255) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=874 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SET FOREIGN_KEY_CHECKS = 1;
4. 创建model类
实体:
/**
* @author 。。。源
* @date 2020/6/22
* @email apple_dzy@163.com
*/
public class SysLog implements Serializable {
private Integer id;
private String userName;
private String operation;
private Integer time;
private String method;
private String params;
private String ip;
private String browser;
private String system;
private Date createTime;
getter and setter ......
}
5. 创建dao和service
mapper:
<select id="selectLog" resultMap="logMap">
select
*
from sys_log
<where>
<if test="userName != null and userName != ''">
user_name like concat('%',#{userName},'%')
</if>
</where>
order by id desc
</select>
dao:
/**
* @author 。。。源
* @date 2020/6/22 2:59 下午
* @email apple_dzy@163.com
*/
@Mapper
public interface SysLogMapper extends BaseMapper<SysLog> {
List<SysLog> selectLog(Pagination page, @Param("userName")String userName);
}
service:
/**
* @author 。。。源
* @date 2020/6/22 2:59 下午
* @email apple_dzy@163.com
*/
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {
@Override
public Page<SysLog> queryLog(Page page, String userName) {
return page.setRecords(baseMapper.selectLog(page,userName));
}
}
/**
* @author 。。。源
* @date 2020/6/22 3:02 下午
* @email apple_dzy@163.com
*/
public interface SysLogService extends IService<SysLog> {
Page<SysLog> queryLog(Page page, String userName);
}
6. 配置Aspect
/**
* @author 。。。源
* @date 2020/6/22 3:06 下午
* @email apple_dzy@163.com
*/
@Aspect
@Component
public class LogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.initial.annotation.Log)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object result = null;
long beginTime = System.currentTimeMillis();
try {
// 执行方法
result = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
Log logAnnotation = method.getAnnotation(Log.class);
if (logAnnotation != null) {
// 注解上的描述
sysLog.setOperation(logAnnotation.value());
}
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
String params = "";
for (int i = 0; i < args.length; i++) {
params += " " + paramNames[i] + ": " + args[i];
}
sysLog.setParams(params);
}
// 获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
// 设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//获取用户名
Subject subject = SecurityUtils.getSubject();
SysUser user = (SysUser) subject.getPrincipal();
if (user != null) {
sysLog.setUserName(user.getUname());
}
//获取执行时长
sysLog.setTime((int) time);
//获取请求时间
sysLog.setCreateTime(new Date());
//获取系统和浏览器
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
if (userAgent != null) {
Browser browser = userAgent.getBrowser();
if (browser != null) {
Version browserVersion = userAgent.getBrowserVersion();
if (browserVersion != null) {
sysLog.setBrowser(browser.getName() + "/" + browserVersion.getVersion());
} else {
sysLog.setBrowser(browser.getName());
}
}
OperatingSystem operatingSystem = userAgent.getOperatingSystem();
if (operatingSystem != null) {
sysLog.setSystem(operatingSystem.getName());
}
}
// 保存系统日志
sysLogService.insert(sysLog);
}
}
7. 配置工具类
/**
* 获取用户真实的ip地址
* @author 。。。源
* @date 2020/6/22 3:12 下午
* @email apple_dzy@163.com
*/
public class IPUtils {
/**
* 获取IP地址
* <p>
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
//X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
String unknown = "unknown";
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
8. 测试
/**
* @author 。。。源
* @date 2020/7/24
* @email apple_dzy@163.com
*/
@RestController
@RequestMapping("/sys_log")
public class SysLogController {
@Autowired
private SysLogService logService;
@Log("查询系统日志")
@PostMapping("/query")
public Json query(@RequestBody String body) {
JSONObject json = JSON.parseObject(body);
String userName = json.getString("userName");
IPage<SysLog> page = logService.queryLog(PageUtils.getPageParam(json), userName);
return Json.succ(oper).data("page", page);
}
}
版权属于:。。。源
本文链接:http://www.findmyfun.cn/spring-boot-uses-aop-to-get-users-operation-and-realize-logging.html
转载时须注明出处及本声明。
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:
https://cloud.tencent.com/developer/support-plan?invite_code=2py75w7904qok