最近新项目要记录行为日志,很久没有用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. hadoop 伪分布配置

    配置 Hadoop 伪分布式 任务配置说明: VMware 15 Centos 6.5 java -jdk 1.8 hadoop-2.6.0-cdh5.14.0.tar.gz 第一步 自行安装虚拟机 ...

  2. Scala学习系列(三)——入门与基础

    本课程源码共享于 https://github.com/tree1123/learning-scala 首先,打开IDEA编辑器的SbtExampleProject项目,我们将在这个项目下进行练习 本 ...

  3. c++使用cin、cout与c中使用scanf、printf进行输入输出的效率问题

    在c++中,我们使用cin和cout进行输入输出会比用scanf和printf更加简洁和方便,但是当程序有大量IO的时候,使用cin和cout进行输入输出会比用scanf和printf更加耗时, 在数 ...

  4. webug3.0靶场渗透基础Day_2(完)

    第八关: 管理员每天晚上十点上线 这题我没看懂什么意思,网上搜索到就是用bp生成一个poc让管理员点击,最简单的CSRF,这里就不多讲了,网上的教程很多. 第九关: 能不能从我到百度那边去? 构造下面 ...

  5. eclipse安装Axis2插件和简单的webservice发布

    2019独角兽企业重金招聘Python工程师标准>>> Axis2与CXF是现在很主流的WebService开发框架(java6也已经支持了),项目上还都是基本上用前两种做开发,今天 ...

  6. CSS开发技巧(二):表格合并边框后的单元格宽度计算

    前言: 分离边框模型和合并边框模型是表格的两种模型,它通过以下属性确定: border-collapse:separate(默认值) | collapse | inherit 当采用分离边框模型时,表 ...

  7. 关于IE8上传文件的一些问题

    问题1: IE8下上传完文件后,对后台返回的JSON格式的数据,浏览器提示了下载该文件. 原因是因为IE8还不支持'application/json"类型的响应. 解决方法将后台返回的JSO ...

  8. itchat学习

    itchat是一个开源的微信个人号接口,可以很方便的使用python调用微信. 教程如下:https://itchat.readthedocs.io/zh/latest/ 简单试玩了一下,觉得还挺有趣 ...

  9. BlackNurse攻击:4Mbps搞瘫路由器和防火墙

    研究人员宣称,最新的知名漏洞BlackNurse,是一种拒绝服务攻击,能够凭借仅仅15到18Mbps的恶意ICMP数据包就将防火墙和路由器干掉. 该攻击会滥用Internet控制报文协议(ICMP)第 ...

  10. 由JS数组去重说起

    一.问题描述: var array=[1,45,3,1,4,67,45],请编写一个函数reDup来去掉其中的重复项,即 reDup(array); console.log(array);//[1,4 ...