@aspect注解_Spring boot自定义注解方式实现日志记录,这种开发方式你用过吗

大家好,我是程序员7歌!

今天我将为大家讲解如何通过自定义注解记录接口访问日志。一般的开发中,有两种方式可以记录日志信息,第一种:把接口日志信息保存到日志文件中,第二种:把接口操作日志保存到数据库中,这里我将为大家讲解第二种方式。

创建日志表

在数据库新增日志记录表,字段我们可以自定义,其中有几个必要字段,如下:

  • type:请求类型
  • title:操作记录(日志标题)
  • remote_addr:操作IP地址
  • username:操作人
  • request_uri:接口地址
  • http_method:接口类型
  • class_method:请求的接口方法
  • params:请求参数数据
  • session_id:用户session

以下就是数据库表结构:

DROP TABLE IF EXISTS `sys_log`;CREATE TABLE `sys_log` ( `id` bigint(50) NOT NULL AUTO_INCREMENT COMMENT '编号', `type` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '请求类型', `title` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '日志标题', `remote_addr` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '操作IP地址', `username` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '操作用户昵称', `request_uri` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '请求URI', `http_method` varchar(10) COLLATE utf8_bin DEFAULT NULL COMMENT '操作方式', `class_method` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '请求类型.方法', `params` text COLLATE utf8_bin COMMENT '操作提交的数据', `session_id` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT 'sessionId', `response` longtext COLLATE utf8_bin COMMENT '返回内容', `use_time` bigint(11) DEFAULT NULL COMMENT '方法执行时间', `browser` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '浏览器信息', `area` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '地区', `province` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '省', `city` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '市', `isp` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '网络服务提供商', `exception` text COLLATE utf8_bin COMMENT '异常信息', `create_by` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '创建者', `create_date` datetime DEFAULT NULL COMMENT '创建时间', `update_by` bigint(64) DEFAULT NULL, `update_date` datetime DEFAULT NULL, `remarks` varchar(255) COLLATE utf8_bin DEFAULT NULL, `del_flag` bit(1) DEFAULT NULL, PRIMARY KEY (`id`), KEY `sys_log_create_by` (`create_by`) USING BTREE, KEY `sys_log_request_uri` (`request_uri`) USING BTREE, KEY `sys_log_type` (`type`) USING BTREE, KEY `sys_log_create_date` (`create_date`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=150 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='系统日志';

使用自动生成代码工具生成SysLog类的信息:SysLog/SysLogController/SysLogService/SysLogServiceImpl/SysLogMapper/SysLogMapper.xml,结构如下图:

a7dd2f813ed928e5ab98266eecf8abac.png

自定义日志注解

在项目中新增自定义注解,用于注解哪些接口需要记录操作日志,代码如下:

package com.july.annotation;import java.lang.annotation.*;/** * 系统日志注解 * @author zqk * @since 2019/12/5 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface SysLog { String value() default "";}

在定义一个Aspect切面类,里面可以定义切入点和通知,核心代码如下:

package com.july.aspect;import com.alibaba.fastjson.JSONObject;import com.july.entity.SysLog;import com.july.service.SysLogService;import com.july.util.ToolUtil;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.lang.reflect.Method;import java.util.Map;/** * 解析响应数据 * @author zqk * @since 2019/12/5 */@Aspect@Order(5)@Component@Slf4jpublic class WebLogAspect { @Resource private SysLogService sysLogService; private ThreadLocal startTime = new ThreadLocal<>(); private SysLog sysLog = null; @Pointcut("@annotation(com.july.annotation.SysLog)") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) { startTime.set(System.currentTimeMillis()); // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); HttpSession session = (HttpSession) attributes.resolveReference(RequestAttributes.REFERENCE_SESSION); sysLog = new SysLog(); sysLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); sysLog.setHttpMethod(request.getMethod()); //获取传入目标方法的参数 Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { Object o = args[i]; if(o instanceof ServletRequest || (o instanceof ServletResponse) || o instanceof MultipartFile){ args[i] = o.toString(); } } String str = JSONObject.toJSONString(args); sysLog.setParams(str.length()>5000? JSONObject.toJSONString("请求参数数据过长不与显示"):str); String ip = ToolUtil.getClientIp(request); if("0.0.0.0".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || "localhost".equals(ip) || "127.0.0.1".equals(ip)){ ip = "127.0.0.1"; } sysLog.setRemoteAddr(ip); sysLog.setRequestUri(request.getRequestURL().toString()); if(session != null){ sysLog.setSessionId(session.getId()); } MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); com.july.annotation.SysLog mylog = method.getAnnotation(com.july.annotation.SysLog.class); if(mylog != null){ //注解上的描述 log.info("注解信息 ===> " + mylog.value()); sysLog.setTitle(mylog.value()); } Map browserMap = ToolUtil.getOsAndBrowserInfo(request); sysLog.setBrowser(browserMap.get("os")+"-"+browserMap.get("browser")); if(!"127.0.0.1".equals(ip)){ Map map = ToolUtil.getAddressByIP(ToolUtil.getClientIp(request)); sysLog.setArea(map.get("area")); sysLog.setProvince(map.get("province")); sysLog.setCity(map.get("city")); sysLog.setIsp(map.get("isp")); } sysLog.setType(ToolUtil.isAjax(request)?"Ajax请求":"普通请求"); sysLog.setUsername("自定义用户"); } @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { try { Object obj = proceedingJoinPoint.proceed(); return obj; } catch (Exception e) { e.printStackTrace(); sysLog.setException(e.getMessage()); throw e; } } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) { sysLog.setUsername("自定义用户"); String retString = JSONObject.toJSONString(ret); sysLog.setResponse(retString.length()>5000? JSONObject.toJSONString("请求参数数据过长不与显示"):retString); sysLog.setUseTime(System.currentTimeMillis() - startTime.get()); sysLogService.save(sysLog); }}

切面类里面设计到几个工具类:

  1. ToolUtil=>getClientIp(获取客户端ip信息)
  2. ToolUtilgetAddressByIP:解析IP信息(调用第三方接口)

编程接口类

在项目里面新增测试接口类,在需要记录日志的接口上面添加注解@SysLog,如下代码:

package com.july.controller;import com.july.annotation.SysLog;import com.july.dto.UserLoginDto;import com.july.dto.UserRedisDto;import com.july.entity.Userinfo;import com.july.service.UserinfoService;import com.july.util.Result;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/** * 前端控制器 * @author zqk * @since 2019/12/4 */@RestController@RequestMapping("/userinfo")public class UserinfoController { @Resource private UserinfoService userinfoService; /** * @description 用户登录 * @param userLoginDto * @return * @author zqk * @since 2019/12/4 */ @SysLog("登录") @PostMapping("/login") public Result login(@RequestBody UserLoginDto userLoginDto){ return Result.ok(userinfoService.userInfoLogin(userLoginDto)); }}

接下来我们启动项目,项目启动成功后,访问登录接口,如下图:

c5f4942809e829b5f3882844b67f102b.png

等接口响应成功后,我们打开数据库去sys_log表里面查看记录信息,记录信息如下:

2d329be13aa1ae24aa660bb83eeaae44.png

从数据库记录我们得知,接口调用的全部信息,这样以来也方便开发人员排查操作记录。对于上面的自定义注解记录操作日志,大家觉得怎么样啊?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值