AOP行为日志
最近新项目要记录行为日志,很久没有用AOP,研究了一下。
废话不多说,先上个流程图:

数据库日志表设计
| 字段名称 | 字段类型 | 注释 |
| LOG_ID | VARCHAR2(255) | |
| LOG_LEVEL | NUMBER | 日志级别 |
| START_TIME | DATE | 开始时间 |
| RUN_TIME | NUMBER | 运行时间(ms) |
| OPERATION_MODULE | VARCHAR2(255) | 被操作的模块 |
| OPERATION_UNIT | VARCHAR2(255) | 被操作的单元 |
| OPERATION_TYPE | VARCHAR2(255) | 操作类型 |
| OPERATION_DETAIL | VARCHAR2(500 CHAR) | 操作详情 |
| USER_CODE | VARCHAR2(255) | 用户编号 |
| USER_NAME | VARCHAR2(255) | 用户名称 |
注:数据库使用的Oracle
JAVA端
1、创建日志实体类
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import java.util.Date; @Data
public class OperationLog {
private String logId;
private String userCode;
private String userName;
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date startTime;
private Long runTime;
private String operationUnit;
private String operationType;
private String operationDetail;
private String operationModule;
private Integer logLevel;
}
2、创建日志操作类型、单元、模块等枚举类
(1)操作模块枚举类
public enum OperationModule {
/**
* 被操作的模块
*/
UNKNOWN("XX系统"),
USER("用户模块"),
PRODUCT("产品模块"),
SALE("销售信息模块");
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
OperationModule(String s) {
this.value = s;
}
}
(2)操作单元枚举类
public enum OperationUnit {
/**
* 被操作的单元
*/
UNKNOWN(""),
/**
* 用户模块
*/
USER_INFO("用户信息"),
USER_ROLE("用户角色"),
USER_PERMISSION("用户权限"),
/**
* 产品模块
*/
PRODUCT_INFO("产品信息"),
PRODUCT_INV("产品库存"),
/**
* 销售信息模块
*/
SALE_INFO("销售信息"),
SALE_PLAN("销售计划");
private String value;
OperationUnit(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
(3)操作类型枚举类
public enum OperationType {
/**
* 基本操作类型
*/
UNKNOWN(""),
LOGIN("登录"),
INPUT("导入"),
QUERY("查询"),
EXPORT("导出"),
DELETE("删除"),
INSERT("插入"),
UPDATE("更新"),
/**
* 用户
*/
USER_SET_ROLE("设置用户角色"),
USER_SET_PERMISSION("设置用户权限"),
/**
* 商品
*/
PRODUCT_EXPORT_INFO("导出商品信息"),
PRODUCT_SET_RANK("设置商品级别"),
/**
* 销售
*/
SALE_EXPORT_INFO("导出销售信息"),
SALE_SET_SALE_PLAN("设置销售计划");
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
OperationType(String s) {
this.value = s;
}
}
3、创建日志注解
import com.XXX.XXX.domin.OperationModule;
import com.XXX.XXX.domin.OperationType;
import com.XXX.XXX.domin.OperationUnit; import java.lang.annotation.*; @Documented //表明这个注解应该被 javadoc工具记录
@Target({ElementType.METHOD}) //声明该注解作用于方法之上
@Retention(RetentionPolicy.RUNTIME) //声明该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
public @interface OperationLogDetail { /**
* 方法描述,可使用占位符获取参数:{{param}}
*/
String operationDetail() default ""; /**
* 日志等级:1-9
*/
int logLevel() default 1; /**
* 操作类型(enum)
*/
OperationType operationType() default OperationType.UNKNOWN; /**
* 被操作的对象(此处使用enum)
*/
OperationUnit operationUnit() default OperationUnit.UNKNOWN; /**
* 被操作的系统模块(此处使用enum)
*/
OperationModule operationModule() default OperationModule.UNKNOWN; }
4、创建AOP方法,使用了环绕通知
@Aspect //表明该类是一个切面
@Component //实例化到spring容器中
public class OperationLogAop { @Autowired
private OperationLogService operationLogService; //表明切点在加了OperationLogDetail注解的方法
@Pointcut("@annotation(com.topsports.adbuhuo.annotation.OperationLogDetail)")
public void operationLog(){} //环绕通知
@Around("operationLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object res = null;
//获取系统当前时间
long time = System.currentTimeMillis();
try {
//获取切入点(要记录日志的方法)的参数
Object[] args = joinPoint.getArgs();
//调用要记录日志的方法
res = joinPoint.proceed(args);
//获取方法执行时长
time = System.currentTimeMillis() - time;
return res;
} finally {
try {
//方法执行完成后增加日志
addOperationLog(joinPoint,res,time);
}catch (Exception e){
System.out.println("LogAspect 操作失败:" + e.getMessage());
e.printStackTrace();
}
}
} private void addOperationLog(JoinPoint joinPoint, Object res, long time){
//获取当前登录的用户
UserInfo userInfo = SecurityUserUtil.getThisUserInfo();
//获取方法的签名,用来获取加在方法上的注解
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
//创建日志对象
OperationLog operationLog = new OperationLog();
operationLog.setRunTime(time);
operationLog.setLogId(UUID.randomUUID().toString());
operationLog.setStartTime(new Date());
operationLog.setUserName(userInfo.getUserName());
operationLog.setUserCode(userInfo.getUserCode());
//获取加在方法上的注解
OperationLogDetail annotation = signature.getMethod().getAnnotation(OperationLogDetail.class);
if(annotation != null){
operationLog.setLogLevel(annotation.logLevel());
operationLog.setOperationDetail(getDetail(((MethodSignature)joinPoint.getSignature()).getParameterNames(),joinPoint.getArgs(),annotation));
operationLog.setOperationType(annotation.operationType().getValue());
operationLog.setOperationUnit(annotation.operationUnit().getValue());
operationLog.setOperationModule(annotation.operationModule().getValue());
}
//保存日志
operationLogService.insertSystemLog(operationLog);
} /**
* 对占位符处理
* @param argNames 方法参数名称数组
* @param args 方法参数数组
* @param annotation 注解信息
* @return 返回处理后的描述
*/
private String getDetail(String[] argNames, Object[] args, OperationLogDetail annotation){ Map<Object, Object> map = new HashMap<>(4);
for(int i = 0;i < argNames.length;i++){
map.put(argNames[i],args[i]);
}
//获取详情信息
String detail = annotation.operationDetail();
try {
//遍历传入方法的参数
for (Map.Entry<Object, Object> entry : map.entrySet()) {
Object k = entry.getKey();
Object v = entry.getValue();
//request和response不可序列化,XSSFWorkbook也不可序列化
if(!(v instanceof HttpServletRequest) && !(v instanceof HttpServletResponse) && !(v instanceof XSSFWorkbook)){
if(v instanceof JSONObject){
//处理JSONObject格式的参数
JSONObject jsonObject = (JSONObject) v;
for (String jk : jsonObject.keySet()) {
detail = detail.replace("{{" + jk + "}}", jsonObject.get(jk)!=null?jsonObject.get(jk).toString():"");
}
}else{
detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
}
}else if(v instanceof HttpServletRequest){
//处理HttpServletRequest
JSONObject jsonObject = CommonUtil.request2Json((HttpServletRequest) v);
for (String jk : jsonObject.keySet()) {
detail = detail.replace("{{" + jk + "}}", jsonObject.get(jk)!=null?jsonObject.get(jk).toString():"");
}
}
}
}catch (Exception e){
e.printStackTrace();
}
return detail;
} }
5、创建Service与Mapper
public interface OperationLogService {
//插入
void insertSystemLog(OperationLog operationLog);
}
--------------------------------------------------
@Service
public class OperationLogServiceImpl implements OperationLogService {
@Autowired
private OperationLogMapper operationLogMapper;
@Override
public void insertSystemLog(OperationLog operationLog) {
operationLogMapper.insertSystemLog(operationLog);
}
}
--------------------------------------------------
@Mapper
@Repository
public interface OperationLogMapper {
void insertSystemLog(OperationLog operationLog);
}
---------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.XXX.XXX.dao.OperationLogMapper">
<insert id="insertSystemLog" parameterType="com.XXX.XXX.domin.OperationLog">
insert into SYSTEM_OPERATION_LOG(
LOG_ID,
USER_CODE,
USER_NAME,
START_TIME,
RUN_TIME,
OPERATION_UNIT,
OPERATION_TYPE,
OPERATION_DETAIL,
LOG_LEVEL,
OPERATION_MODULE
)
values(
#{logId},
#{userCode},
#{userName},
#{startTime},
#{runTime},
#{operationUnit},
#{operationType},
#{operationDetail},
#{logLevel},
#{operationModule}
)
</insert>
</mapper>
使用
在需要记录日志的方法上添加创建的注解
@OperationLogDetail(
operationDetail = "{{userCode}}", //该占位符将在创建日志对象时扫描参数列表获取
operationType = OperationType.QUERY,
operationUnit = OperationUnit.USER_INFO,
operationModule = OperationModule.USER)
@PostMapping("/getUserInfo")
public JSONObject getUserInfo(@RequestBody JSONObject jsonObject){
return userInfoService.getUserInfo(jsonObject);
}
AOP行为日志的更多相关文章
- Spring AOP 完成日志记录
Spring AOP 完成日志记录 http://hotstrong.iteye.com/blog/1330046
- Spring AOP进行日志记录
在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个 ...
- [置顶] 使用sping AOP 操作日志管理
记录后台操作人员的登陆.退出.进入了哪个界面.增加.删除.修改等操作 在数据库中建立一张SYSLOG表,使用Sping 的AOP实现日志管理,在Sping.xml中配置 <!-- Spring ...
- Spring AOP进行日志记录,管理
在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个 ...
- Spring Boot 入门(五):集成 AOP 进行日志管理
本篇文章是接着 Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理写的,按照前面几篇博客的教程,可以搭建一个简单的项目,主要包含了 Pagehelper+MyBatis 分页 ...
- spring aop实现日志收集
概述 使用spring aop 来实现日志的统一收集功能 详细 代码下载:http://www.demodashi.com/demo/10185.html 使用spring aop 来实现日志的统一收 ...
- 自定义注解-aop实现日志记录
关于注解,平时接触的可不少,像是 @Controller.@Service.@Autowried 等等,不知道你是否有过这种疑惑,使用 @Service 注解的类成为我们的业务类,使用 @Contro ...
- AOP拦截日志类,抛异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode
AOP的日志拦截类中,抛出异常: java.lang.IllegalStateException: It is illegal to call this method if the current r ...
- 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘
前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国 ...
- 【Java EE 学习 77 上】【数据采集系统第九天】【通过AOP实现日志管理】【通过Spring石英调度动态生成日志表】【日志分表和查询】
一.需求分析 日志数据在很多行业中都是非常敏感的数据,它们不能删除只能保存和查看,这样日志表就会越来越大,我们不可能永远让它无限制的增长下去,必须采取一种手段将数据分散开来.假设现在整个数据库需要保存 ...
随机推荐
- 新的知识点来了-ES6 Proxy代理 和 去银行存款有什么关系?
ES给开发者提供了一个新特性:Proxy,就是代理的意思.也就是我们这一节要介绍的知识点. 以前,ATM还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...
- 图解Python的垃圾回收机制
Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的 ...
- php中垃圾回收机制
php中垃圾回收机制 我们可能在开发中经常会听到gc,是的gc就是垃圾回收容器,全称Garbage Collection. 此篇文章中“垃圾”的概念:如果一个变量容器能被减少到0,说明他就已经没有被引 ...
- spark2.4.5计算框架中各模块的常用实例
本项目是使用scala语言给出了spark2.4.5计算框架中各模块的常用实例. 温馨提醒:spark的版本与scala的版本号有严格的对应关系,安装请注意. Spark Core RDD以及Pair ...
- application/x-www-form-urlencoded ,multipart/form-data, text/plain
APPLICATION/X-WWW-FORM-URLENCODED MULTIPART/FORM-DATA TEXT/PLAIN 后台返回的数据响应的格式类型 application/x-www-fo ...
- Git (一)预设环境和免密登录
背景 一直用的svn,这段时间换了之后才发现git的强大功能.缺点就是可能上手比较难一点. 接下来就带你Git入门 Git是什么? Git是目前世界上最先进的分布式版本控制系统 Git有什么特点?好用 ...
- 如何在没有安装 Python 的机器上运行 Python 程序
cmd 命令 1. pip install pyinstaller 2. pyinstaller <path to your prog.py> 3. 应用程序在prog.py同路径下的 d ...
- Python 删除含有只读文件(夹)的文件夹
def rm_read_only(fn, tmp, info): if os.path.isfile(tmp): os.chmod(tmp, stat.S_IWRITE) os.remove(tmp) ...
- Linux 文件常用权限
-rw------- (600) 只有所有者才有读和写的权限 -rw-r--r-- (644) 只有所有者才有读和写的权限,组群和其他人只有读的权限 -rwx------ (700) 只有所有者才有读 ...
- OpenCV学习(1)——初步接触
一.介绍OpenCV OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库.OpenCV是由英特尔公司发起并参与开 ...