最近新项目要记录行为日志,很久没有用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行为日志的更多相关文章

  1. Spring AOP 完成日志记录

    Spring AOP 完成日志记录 http://hotstrong.iteye.com/blog/1330046

  2. Spring AOP进行日志记录

    在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个 ...

  3. [置顶] 使用sping AOP 操作日志管理

    记录后台操作人员的登陆.退出.进入了哪个界面.增加.删除.修改等操作 在数据库中建立一张SYSLOG表,使用Sping 的AOP实现日志管理,在Sping.xml中配置 <!-- Spring ...

  4. Spring AOP进行日志记录,管理

    在java开发中日志的管理有很多种.我一般会使用过滤器,或者是Spring的拦截器进行日志的处理.如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个 ...

  5. Spring Boot 入门(五):集成 AOP 进行日志管理

    本篇文章是接着 Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理写的,按照前面几篇博客的教程,可以搭建一个简单的项目,主要包含了 Pagehelper+MyBatis 分页 ...

  6. spring aop实现日志收集

    概述 使用spring aop 来实现日志的统一收集功能 详细 代码下载:http://www.demodashi.com/demo/10185.html 使用spring aop 来实现日志的统一收 ...

  7. 自定义注解-aop实现日志记录

    关于注解,平时接触的可不少,像是 @Controller.@Service.@Autowried 等等,不知道你是否有过这种疑惑,使用 @Service 注解的类成为我们的业务类,使用 @Contro ...

  8. 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 ...

  9. 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘

    前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国 ...

  10. 【Java EE 学习 77 上】【数据采集系统第九天】【通过AOP实现日志管理】【通过Spring石英调度动态生成日志表】【日志分表和查询】

    一.需求分析 日志数据在很多行业中都是非常敏感的数据,它们不能删除只能保存和查看,这样日志表就会越来越大,我们不可能永远让它无限制的增长下去,必须采取一种手段将数据分散开来.假设现在整个数据库需要保存 ...

随机推荐

  1. ubuntu-18.0.4 samba安装

    (1)安装 sudo apt-get -y install samba samba-common (2)创建一个用于分享的samba目录. mkdir /home/myshare (3)给创建的这个目 ...

  2. C++基础 学习笔记五:重载之运算符重载

    C++基础 学习笔记五:重载之运算符重载 什么是运算符重载 用同一个运算符完成不同的功能即同一个运算符可以有不同的功能的方法叫做运算符重载.运算符重载是静态多态性的体现. 运算符重载的规则 重载公式 ...

  3. jarvisoj MISC 取证2

    打开之后一个文件和一个镜像 TrueCrypt....记住他了,再看一眼那个文件,好的,TrueCrypt加密..找密码 把Truecrypt.exe直接dump下来,用efdd解密就行了

  4. jmeter DB2数据库连接与操作

    1.需要把数据库连接jar包拷贝到 jmeter lib目录下 先创建一个数据库连接配置元件 2.添加jdbc请求(我用的后置处理器) 3.可以通过beanshell 对结果集进行操作 beanshe ...

  5. HBase Filter 过滤器概述

    abc 过滤器介绍 HBase过滤器是一套为完成一些较高级的需求所提供的API接口. 过滤器也被称为下推判断器(push-down predicates),支持把数据过滤标准从客户端下推到服务器,带有 ...

  6. python 利用numpy同时打乱列表的顺序,同时打乱数据和标签的顺序

    可用于网络训练打乱训练数据个标签,不改变对应关系 方法一: np.random.shuffle (无返回值,直接打乱原列表) state = np.random.get_state() np.rand ...

  7. MySQL基础知识和常用命令总结

    说明:以下内容是阅读书籍<<MySQL必知必会>>的摘要和总结 检索数据 排序检索数据 过滤数据 使用通配符过滤 使用正则表达式进行搜索 创建计算字段 使用数据处理函数 汇总数 ...

  8. 显示 QStringList 的内容

    QStringList s; s << "your" << "string" << "list"; ; ...

  9. vue项目中使用bpmn-为节点添加颜色

    内容概述 本系列 “vue项目中使用bpmn-xxxx” 分为五篇,均为自己使用过程中用到的实例,手工原创,目前属于陆续更新中.主要包括vue项目中bpmn使用实例.应用技巧.基本知识点总结和需要注意 ...

  10. 有赞透明多级缓存解决方案(TMC)设计思路

    引子 TMC 是什么 TMC,即"透明多级缓存(Transparent Multilevel Cache)",是有赞 PaaS 团队给公司内应用提供的整体缓存解决方案. TMC 在 ...