最近新项目要记录行为日志,很久没有用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. 《并发编程的艺术》阅读笔记之Lock与AQS

    Lock接口 在jdk1.5之后,并发包下新增了一个lock接口,lock接口定义了锁的获取,锁的释放,等方法,需要用户手动设置.与关键字不同的是,lock具有可操作性,比如,可以中断线程,设置超时时 ...

  2. Python爬虫---爬取腾讯动漫全站漫画

    目录 操作环境 网页分析 明确目标 提取漫画地址 提取漫画章节地址 提取漫画图片 编写代码 导入需要的模块 获取漫画地址 提取漫画的内容页 提取章节名 获取漫画源网页代码 下载漫画图片 下载结果 完整 ...

  3. PHP(ThinkPHP5.0) + PHPMailer 进行邮箱发送验证码

    GitHub下载最新版第三方类库PHPMailer: 第一步: 打开网址https://github.com/PHPMailer/PHPMailer/ 下载PHPMailer,PHPMailer 需要 ...

  4. wincache 与 zend guard 的冲突

    ZendLoader.dll 与wincache.dll  同时开启 问题分析:zend与wincache冲突 解决方法: 关掉wincache: 在php.ini中的 extension=php_w ...

  5. 点击 QTableView,触发事件

    Here is an example of how you can get a table cell's text when clicking on it. Suppose a QTableView ...

  6. [Windows] 如何用编程的方式格式化硬盘

    If memory serves, you're looking for SHFormatDrive(). Use the Format method of the Win32_Volume clas ...

  7. Android:finish()与System.exit(0)之间的区别

    finish()与System.exit(0)都是用来退出.但是两者还是有一定的区别: finish是Activity的类,仅仅针对Activity,当调用finish()时,只是将活动推向后台,并没 ...

  8. Nginx重写请求后将url?后的参数去除

    2019独角兽企业重金招聘Python工程师标准>>> 使用?结尾     注意,关键点就在于"?"这个尾缀.重定向的目标地址结尾处如果加了?号,则不会再转发传递 ...

  9. 前端程序员难翻身,没有好的学习方法,你永远无法成功,vue.js专题

    学习vue正确思路,是先学vue-cli,再学vue.js单文件引用的用法,这样会在极短时间内撤底撑握vue, 如果先学vue.js单文件用法,再去学vue-cli4,可以说是重新学vue,,,,难处 ...

  10. 数学--数论--HDU 12151七夕节 Plus (因子和线性筛)

    Problem Description 七夕节那天,月老来到数字王国,他在城门上贴了一张告示,并且和数字王国的人们说:"你们想知道你们的另一半是谁吗?那就按照告示上的方法去找吧!" ...