一、设计:
操作日志记录 根据业务场景 一般是需要记录下数据修改更新的日志,查询类可以忽略。 所以需要对指定的某些方法进行记录。这块希望可以结合注解灵活操作,对于注解的方法进行日志记录
操作日志表设计如下:
CREATE TABLE `op_log` (
`id` bigint(20) NOT NULL COMMENT '主键',
`operator` varchar(32) NOT NULL COMMENT '操作者',
`method` varchar(100) NOT NULL COMMENT '请求方法',
`request_url` varchar(125) NOT NULL COMMENT '请求URL',
`param` varchar(512) NOT NULL COMMENT '参数',
`ip` int(10) NOT NULL COMMENT '请求IP',
`create_time` bigint(20) NOT NULL COMMENT '创建时间',
`op_time` bigint(20) NOT NULL COMMENT '操作时间',
`is_valid` tinyint(4) NOT NULL COMMENT '是否有效(0-无效 1-有效)',
`last_ver` int(8) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`),
KEY `idx_request_url` (`request_url`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='操作记录表'
web层应用入口直接进行日志记录,有两个选项,通过拦截器过滤器在http请求之前或者之后搞事情,通过切面,对着两个方案进行评估,最后选择了切面来进行记录
1.拦截器 过滤器 可以获取到httpRequest信息 从而获取请求IP等属性 ,但是对于某些请求我们不需要记录日志,灵活性上面很差,并且获取不到方法名称,类信息,无法跟注解结合使用
2.切面有org.aspectj.lang.ProceedingJoinPoint连接点对象,可以获取目标对象,切点的参数等等,对于ip和会话信息获取不到,可以采用threadLocal每个连接维护session的方法
threadLocal调研:主要是防止内存泄漏,用户每次点击进入web页面,都会有一个线程分配,每个线程维护自己的threadLocal保存会话信息,如果生命周期长并且用户连接数多,容易导致GC
看过源码之后,Thread内部维护了一个ThreadLocal.ThreadLocalMap threadLocals成员变量,通过threadLocal的set方法存储的独享变量会存储在每个线程自己的threadLocals里面
threadLocal的原理可以看下这篇文章 很不错:
http://www.cnblogs.com/dolphin0520/p/3920407.html
切面:
@within @annotation
@within(com.cxh.study.aop.controller.UserAccessAnnotation)
表示拦截含有com.cxh.study.aop.controller.UserAccessAnnotation这个注解的类中所有方法
@annotation(com.cxh.study.aop.controller.UserAccessAnnotation)
表示拦截含有这个注解的方法
静态的threadLocal声明为全局变量,用于类之间传递,避免创建过多对象

二、代码实现

1.自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {/*** 方法** @return*/String method();
}

2.ThreadLocal存储httpServletRequest

public class RequestManager {private static ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<HttpServletRequest>();/*** 当前线程加入request** @param request*/public static void setHttpServletRequest(HttpServletRequest request) {if (request != null) {threadLocal.set(request);}}/*** 当前线程获取request*/public static HttpServletRequest getHttpServletRequest() {return threadLocal.get();}/*** 清理request,释放空间*/public static void removeHttpServletRequest() {threadLocal.remove();}
}
3.拦截器配置
public class CommonFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {try {RequestManager.setHttpServletRequest(request);filterChain.doFilter(request, response);} finally {//防止出现内存泄露RequestManager.removeHttpServletRequest();}}
}

4.切面存储操作日志

@Service
@Aspect
public class OpLogSaveAspect {@Resourceprivate RedisSentinelService cartCacheService;@Resourceprivate IOpLogService opLogService;@Pointcut(value = "@annotation(com.dfire.soa.consumer.faw.annotation.OperationLog) ")public void operationLog() {}@Around(value = "operationLog()")public Object aroundService(ProceedingJoinPoint pjp) throws Throwable {Object obj;try {obj = pjp.proceed();saveOpLog(pjp);} catch (Throwable t) {throw t;}return obj;}private void saveOpLog(ProceedingJoinPoint pjp) {//获取用户信息HttpServletRequest request = RequestManager.getHttpServletRequest();if (request == null) {return;}AccountInfo currentUser = currentAccount(request);if (currentUser == null) {return;}MethodSignature methodSignature = (MethodSignature) pjp.getSignature();String method = this.getMethodAnnotation(methodSignature, pjp.getTarget().getClass());if (StringUtils.isBlank(method)) {return;}Object[] objects = pjp.getArgs();if (objects == null || objects.length == 0) {return;}List<Object> params = Lists.newArrayList();for(Object object : objects){if(object instanceof HttpServletRequest){continue;}params.add(object);}OpLogDTO opLogDTO = new OpLogDTO();opLogDTO.setOperator(currentAccount(request).getUsername());opLogDTO.setRequestUrl(request.getRequestURL().toString());opLogDTO.setMethod(method);opLogDTO.setParam(JSON.toJSONString(params));opLogDTO.setIp(request.getRemoteHost());opLogService.recordOpLog(opLogDTO);}/*** 取方法注释.** @param cls* @return the method annotation* @throws SecurityException* @throws NoSuchMethodException*/String getMethodAnnotation(MethodSignature methodSignature, Class<? extends Object> cls) {Method method = methodSignature.getMethod();try {method = cls.getMethod(method.getName(), method.getParameterTypes());OperationLog operationLog = method.getAnnotation(OperationLog.class);if (operationLog != null) {return operationLog.method();}} catch (NoSuchMethodException e) {} catch (SecurityException e) {}return null;}private AccountInfo currentAccount(HttpServletRequest request) {String token = request.getParameter("token");// 把 token 当作key 值从 redis 里面获取return JSON.parseObject(cartCacheService.get(Constants.TokenKey.VERIFY_CODE_TOKEN_KEY + token), AccountInfo.class);}}
5.最后一步 添加注解
@RequestMapping(value = "/v1/modify", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
@OperationLog(method = "modifyActivity")
public ResultMap modify(HttpServletRequest request,@RequestBody PreSellActivityModifyDTO preSellActivityModifyDTO) throws Exception {

数据库存储如下:

AOP实现操作日志记录相关推荐

  1. JPOM - AOP+自定义注解实现操作日志记录

    文章目录 地址 版本 源码解析-AOP+自定义注解实现操作日志记录 地址 Gitee: https://gitee.com/dromara/Jpom 官网: https://jpom.io/ 一款简而 ...

  2. 我们已经不用AOP做操作日志了!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 前言 用户在操作我们系统的过程中,针对一些重要的业务数据进行增删改 ...

  3. 我们已经不用AOP做操作日志了! | 原力计划

    来源 | JAVA葵花宝典 责编 | 王晓曼.Carol  头图 | CSDN下载自东方IC 前言 用户在操作我们系统的过程中,针对一些重要的业务数据进行增删改查的时候,我们希望记录一下用户的操作行为 ...

  4. java aop注解日志记录_springMVC自定义注解,用AOP来实现日志记录的方法

    需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中. 为了保证工期,在查阅了资料以后,决定用AOP+ ...

  5. 如何实现操作操作日志记录

    如何实现操作操作日志记录 为什么要记录操作日志? 项目中的业务需求,需要针对用户的一些业务操作做操作记录, 也就是标题中的操场日志记录,最近做的项目也有这个需求, 我也是第一次写,相信有很多开发者也有 ...

  6. 共享服务器文件归档,如何实现共享文件访问记录方法、共享文件管理和共享文件操作日志记录.docx...

    文档 文档 PAGE / NUMPAGES 文档 如何实现共享文件访问记录方法.共享文件管理和共享文件操作日志记录 企业单位,办公都有应用到共享文件,经常会在文件共享服务器上存储单位一些重要的共享文件 ...

  7. Django中间件实现操作日志记录

    Django中间件实现操作日志 本文通过Django中间件的流程,实现操作日志记录的功能,模块化.拿来即用. 功能描述:通过中间件记录 请求时间.操作用户.请求URL.请求方法.请求IP.请求参数和响 ...

  8. qt 历史记录控件_基于Qt图形界面软件的操作日志记录方法及系统_2015106293015_说明书_专利查询_专利网_钻瓜专利网...

    技术领域 本发明涉及一种软件系统的日志记录技术,特别涉及一种基于Qt图形界面软件的操作日志记录方法及系统. 背景技术 软件操作日志是记录用户在使用软件的过程中,通过鼠标和键盘在操作界面上执行的点击和输 ...

  9. MySQL的操作日志记录

    如何配置mysql数据库的操作日志及如何查看mysql的操作日志记录 MySQL的几种操作日志 1.错误日志 log_error (主要是记录启动.运行.停止mysql时出现的致命问题,系统级别的错误 ...

最新文章

  1. 机器学习入门:一文让你快速了解机器学习
  2. 人均 11878 元,2020 年研发岗年终奖最高!技术、产品岗均榜上有名
  3. C++ string流
  4. httpd服务器常见漏洞修复,网站安全监测以及漏洞修复过程
  5. 缓存模式以及缓存的数据一致性
  6. cout的输出格式初探
  7. 转 知道这20个正则表达式,能让你少写1,000行代码
  8. elasticsearch 数据类型_基于 MySQL Binlog 的 Elasticsearch 数据同步实践
  9. 批处理bat命令--获取当前盘符和当前目录和上级目录
  10. mongodb - 查看正在执行的操作
  11. 自定义ClassLoader和双亲委派机制
  12. Zipkin分布式任务追踪
  13. hive练习之join操作
  14. vscode 设置关键字高亮显示
  15. 十大经典排序算法(动图演示)
  16. 文件下载触发的DDE注入
  17. 【高仿微信系列】微信录制小视频
  18. _WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h
  19. aix安装bff_AIX程序打包
  20. 《一斛珠·元夜月蚀》

热门文章

  1. css取第一个孩子,CSS:第n个孩子():后(css :nth-child() :after)
  2. http://www.msftconnecttest.com/redirect找不到应用程序解决办法
  3. android notifydatasetchanged 刷新错误,android – notifyDataSetChanged()不刷新可扩展列表视图...
  4. 黑马程序员 浅谈封装的具体实现的几种例子
  5. 教师能力发展模型【职业生涯规划的好,可以用卖白菜的努力,挣卖白粉的钱】...
  6. 【AI100篇经典论文】Do ImageNet Classifiers Generalize to ImageNet
  7. linux应用端口的网络超时,Linux服务器可以ping,但是telnet端口超时,网站wget超时,访问超时的解决办法...
  8. vulnhub-The Planets: Mercury
  9. 实验1--串口通信小试
  10. iOS Tabbar凸起效果