使用SpringBoot AOP 记录操作日志、异常日志
平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能;我们在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找到,而且也不能对发生的异常进行统计,从而改进我们的项目,要是能做个功能专门来记录操作日志和异常日志那就好了, 当然我们肯定有方法来做这件事情,而且也不会很难,我们可以在需要的方法中增加记录日志的代码,和在每个方法中增加记录异常的代码,最终把记录的日志存到数据库中。听起来好像很容易,但是我们做起来会发现,做这项工作很繁琐,而且都是在做一些重复性工作,还增加大量冗余代码,这种方式记录日志肯定是不可行的。
我们以前学过Spring 三大特性,IOC(控制反转),DI(依赖注入),AOP(面向切面),那其中AOP的主要功能就是将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来。今天我们就来用springBoot Aop 来做日志记录,好了,废话说了一大堆还是上货吧。
一、创建日志记录表、异常日志表,表结构如下:

操作日志表

异常日志表
二、添加Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
三、创建操作日志注解类OperLog.java
package com.hyd.zcar.cms.common.utils.annotation; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 自定义操作日志注解
* @author wu
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperLog {
String operModul() default ""; // 操作模块
String operType() default ""; // 操作类型
String operDesc() default ""; // 操作说明
}
四、创建切面类记录操作日志
package com.hyd.zcar.cms.common.utils.aop; import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import com.gexin.fastjson.JSON;
import com.hyd.zcar.cms.common.utils.IPUtil;
import com.hyd.zcar.cms.common.utils.annotation.OperLog;
import com.hyd.zcar.cms.common.utils.base.UuidUtil;
import com.hyd.zcar.cms.common.utils.security.UserShiroUtil;
import com.hyd.zcar.cms.entity.system.log.ExceptionLog;
import com.hyd.zcar.cms.entity.system.log.OperationLog;
import com.hyd.zcar.cms.service.system.log.ExceptionLogService;
import com.hyd.zcar.cms.service.system.log.OperationLogService; /**
* 切面处理类,操作日志异常日志记录处理
*
* @author wu
* @date 2019/03/21
*/
@Aspect
@Component
public class OperLogAspect { /**
* 操作版本号
* <p>
* 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
* </p>
*/
@Value("${version}")
private String operVer; @Autowired
private OperationLogService operationLogService; @Autowired
private ExceptionLogService exceptionLogService; /**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
*/
@Pointcut("@annotation(com.hyd.zcar.cms.common.utils.annotation.OperLog)")
public void operLogPoinCut() {
} /**
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
*/
@Pointcut("execution(* com.hyd.zcar.cms.controller..*.*(..))")
public void operExceptionLogPoinCut() {
} /**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
*
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operLogPoinCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST); OperationLog operlog = new OperationLog();
try {
operlog.setOperId(UuidUtil.get32UUID()); // 主键ID // 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作
OperLog opLog = method.getAnnotation(OperLog.class);
if (opLog != null) {
String operModul = opLog.operModul();
String operType = opLog.operType();
String operDesc = opLog.operDesc();
operlog.setOperModul(operModul); // 操作模块
operlog.setOperType(operType); // 操作类型
operlog.setOperDesc(operDesc); // 操作描述
}
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName; operlog.setOperMethod(methodName); // 请求方法 // 请求的参数
Map<String, String> rtnMap = converMap(request.getParameterMap());
// 将参数所在的数组转换成json
String params = JSON.toJSONString(rtnMap); operlog.setOperRequParam(params); // 请求参数
operlog.setOperRespParam(JSON.toJSONString(keys)); // 返回结果
operlog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // 请求用户ID
operlog.setOperUserName(UserShiroUtil.getCurrentUserName()); // 请求用户名称
operlog.setOperIp(IPUtil.getRemortIP(request)); // 请求IP
operlog.setOperUri(request.getRequestURI()); // 请求URI
operlog.setOperCreateTime(new Date()); // 创建时间
operlog.setOperVer(operVer); // 操作版本
operationLogService.insert(operlog);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
*
* @param joinPoint 切入点
* @param e 异常信息
*/
@AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST); ExceptionLog excepLog = new ExceptionLog();
try {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
excepLog.setExcId(UuidUtil.get32UUID());
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
// 请求的参数
Map<String, String> rtnMap = converMap(request.getParameterMap());
// 将参数所在的数组转换成json
String params = JSON.toJSONString(rtnMap);
excepLog.setExcRequParam(params); // 请求参数
excepLog.setOperMethod(methodName); // 请求方法名
excepLog.setExcName(e.getClass().getName()); // 异常名称
excepLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())); // 异常信息
excepLog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // 操作员ID
excepLog.setOperUserName(UserShiroUtil.getCurrentUserName()); // 操作员名称
excepLog.setOperUri(request.getRequestURI()); // 操作URI
excepLog.setOperIp(IPUtil.getRemortIP(request)); // 操作员IP
excepLog.setOperVer(operVer); // 操作版本号
excepLog.setOperCreateTime(new Date()); // 发生异常时间 exceptionLogService.insert(excepLog); } catch (Exception e2) {
e2.printStackTrace();
} } /**
* 转换request 请求参数
*
* @param paramMap request获取的参数数组
*/
public Map<String, String> converMap(Map<String, String[]> paramMap) {
Map<String, String> rtnMap = new HashMap<String, String>();
for (String key : paramMap.keySet()) {
rtnMap.put(key, paramMap.get(key)[0]);
}
return rtnMap;
} /**
* 转换异常信息为字符串
*
* @param exceptionName 异常名称
* @param exceptionMessage 异常信息
* @param elements 堆栈信息
*/
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
return message;
}
}
五、在Controller层方法添加@OperLog注解

六、操作日志、异常日志查询功能





使用SpringBoot AOP 记录操作日志、异常日志的更多相关文章
- SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- 让FIREDAC记录数据库的异常日志
默认FIREDAC不会记录数据库的异常. 比如典型的,提交的时候,非空字段没有给值. 某些人还以为FIREDAC不能捕获数据库的异常,其实FIREDAC是可以捕获并处理数据库的异常事件的. 方法异常简 ...
- spring-boot-route(十七)使用aop记录操作日志
在上一章内容中--使用logback管理日志,我们详细讲述了如何将日志生成文件进行存储.但是在实际开发中,使用文件存储日志用来快速查询问题并不是最方便的,一个优秀系统除了日志文件还需要将操作日志进行持 ...
- Spring aop 记录操作日志 Aspect
前几天做系统日志记录的功能,一个操作调一次记录方法,每次还得去收集参数等等,太尼玛烦了.在程序员的世界里,当你的一个功能重复出现多次,就应该想想肯定有更简单的实现方法.于是果断搜索各种资料,终于搞定了 ...
- springboot aop + logback + 统一异常处理 打印日志
1.src/resources路径下新建logback.xml 控制台彩色日志打印 info日志和异常日志分不同文件存储 每天自动生成日志 结合myibatis方便日志打印(debug模式) < ...
- springmvc集成aop记录操作日志
首先说明一下,这篇文章只做了记录日志相关事宜 具体springmvc如何集成配置aop对cotroller进行拦截,请看作者的另一篇文章 http://www.cnblogs.com/guokai87 ...
- Spring aop 记录操作日志 Aspect 自定义注解
时间过的真快,转眼就一年了,没想到随手写的笔记会被这么多人浏览,不想误人子弟,于是整理了一个优化版,在这里感谢智斌哥提供的建议和帮助,话不多说,进入正题 所需jar包 :spring4.3相关联以及a ...
- 用AOP记录操作日志,并写进数据库。
先用AOP注解 1 package com.vlandc.oss.apigate.log.aspect; import java.util.Map; import java.util.Optional ...
- Springboot AOP写操作日志 GET POST
pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
随机推荐
- 【leetcode】835. Image Overlap
题目如下: 解题思路:抛开移动的过程只看移动完成的结果,记图片左上角为顶点,正方形边长为board,要使得两个图片要有重叠,那么一定其中一张图片的顶点和另外一张图片的某一个点重合.假设图片A的顶点A( ...
- 常见算法和数据结构存在的坑(updating)
数组: c++数组下标都+5会稳. 50005000的别开60006000. 二分: 实数二分可能因为神马精度问题出现了不满足二分序的情况,要小心. 注意二分完后,不能直接用当前数组里存的值,要pd( ...
- 存储过程中的in out in out 三种类型的参数
in 是参数的默认模式,这种模式就是在程序运行的时候已经具有值,在程序体中值不会改变. out模式定义的参数只能在过程体内部赋值,表示该参数可以将某个值传递回调用他的过程 in out 表示高参数可以 ...
- 在树莓派3B上搭建LAMP
一.安装apache2 sudo apt-get install apache2 在电脑上输入树莓派的网址会有如下显示 二.安装Mysql sudo apt-get install mysql-ser ...
- java web项目的https配置
1.进入到jdk下的bin目录 keytool -v -genkey -alias tomcat -keyalg RSA -keystore d:/tomcat.keystore -validity ...
- sql server 基础语法4 实践练习+子查询
drop table class create table class ( classId ) primary key not null, cName ) ) insert into class ', ...
- Python基础教程(008)--第一个Python程序
前言: 学会第一个Python程序 了解Python2和Python3的区别 内容 执行Python程序的三种方式 解释器--Python.Python3 交互式-- ipthon 集成开发环境--P ...
- 2017 NOIp 初赛体验
很菜...我还是太蒟蒻了. d 老师太强了... 应该能有七十几分 初赛稳了 Update: 五十几分...
- python 100day notes (1)
x1 + x2 +x3 + x4 = 8 多少正整数解 上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案 即用三个隔板插7个空位. 答案C(7,3) =35 # __name__是Pyt ...
- hbase shell插入根据条件查询数据
hbase shell插入根据条件查询数据 创建并插入数据: hbase(main):179:0> create 'scores','grade','course' hbase(main):18 ...