最近新项目要记录行为日志,很久没有用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. Springboot:员工管理之添加员工(十(7))

    构建员工添加请求 com\springboot\controller\EmployeeController.java /*调转到员工添加页 携带部门信息 restful风格*/ @GetMapping ...

  2. Go语言 2019 调查报告发布

    Go 官方博客昨日公布了[ 2019 年 Go 语言调查报告].本次调查收到的回复达到 10,975 份,约为去年的两倍. 这些受访者的反馈意见将被选取用于改进 Go 语言的发展. 以下是 2019 ...

  3. notepad正则删除关键词所在行

    转自:http://www.gangzi.net/article/615.htm 查找:^.*大师兄.*$替换为:(空) 如果不留空行:查找:^.*大师兄.*\r?\n替换为:(空)注意:Notepa ...

  4. (转)如何学好C++语言

    原文:http://coolshell.cn/articles/4119.html   作者:陈皓 昨天写了一篇如何学好C语言,就有人回复问我如何学好C++,所以,我把我个人的一些学习经验写在这里,希 ...

  5. java中Runnable和Callable的区别

    文章目录 运行机制 返回值的不同 Exception处理 java中Runnable和Callable的区别 在java的多线程开发中Runnable一直以来都是多线程的核心,而Callable是ja ...

  6. mac OS 卸载node.js及npm

    通过homebrew安装的 输入卸载命令 brew uninstall node 通过官网下载pkg安装包的 输入卸载命令 sudo rm -rf /usr/local/{bin/{node,npm} ...

  7. Docker容器利用weave实现跨主机互联

    Docker容器利用weave实现跨主机互联 环境: 实现目的:实现主机A中容器1与主机B中容器1的网络互联 主机A步骤: ①下载复制weave二进制执行文件(需要internet)[root@192 ...

  8. Clickhosue 强大的函数,argMin() 和argMax()函数

    说实话,我喜欢Clickhouse 的函数,简单操作,功能强大.今天需要给大家介绍两个函数,argMin(),argMax() 1.argMax():计算 ‘arg’ 最大值 ‘val’ 价值. 如果 ...

  9. Vim Operations

    Vim有三种模式:输入模式.命令模式和末行命令模式. 输入模式用来输入文字,命令模式用来下达编排文件的操作指令,末行命令模式用来进行文件存档.离开编辑器等操作. 进入及离开 末行模式下: :w 保存当 ...

  10. C语言编程入门题目--No.11

    题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月 后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析: 兔子的规律为数列1,1,2,3, ...