背景

系统出现数据莫名丢失,业务人员的反馈无法复现问题。纠结了很久,最终老板发话要记录操作,通过日志进行分析重现

环境

SSH框架

目标

1、记录访问了那个方法,使用的参数及返回的内容

2、记录新增、修改、删除的数据持久化记录

关键

监听器(Interceptor)

实现

1、访问记录,使用Struts的MethodFilterInterceptor监听器

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.codehaus.jackson.map.ObjectMapper; import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
/**
* Action方法监听
* @author ZhangLi
* @date 2018年8月28日 下午4:42:57
*/
public class MethodInterceptor extends MethodFilterInterceptor { private static final long serialVersionUID = 1L; @SuppressWarnings("rawtypes")
@Override
protected String doIntercept(ActionInvocation AI) throws Exception {
String result = null;
Logger logger = null;
try{
result = AI.invoke();
String methodName = AI.getProxy().getMethod();
if (methodName.length() > 0) {
Object action = AI.getAction();
Class clazz = action.getClass();
logger = Logger.getLogger(clazz);
ObjectMapper mapper = new ObjectMapper(); StringBuilder sbInfo = new StringBuilder();
sbInfo.append("[ Log ] 操作记录 (Start)");
sbInfo.append("\n");
sbInfo.append(String.format("访问后台:%s访问[%s]类[%s]方法",ServletActionContext.getRequest().getRemoteAddr(),clazz.getName(),methodName));
sbInfo.append("\n");
sbInfo.append(String.format("访问URL:%s",ServletActionContext.getRequest().getRequestURI()));
sbInfo.append("\n");
sbInfo.append(String.format("访问参数:%s",mapper.writeValueAsString(ServletActionContext.getRequest().getParameterMap())));
sbInfo.append("\n");
sbInfo.append(String.format("返回结果:%s",result));
sbInfo.append("\n");
sbInfo.append("[ Log ] 操作记录 (End)"); logger.warn(sbInfo);
}
}catch(Exception e){
logger.error("Log - 访问错误", e);
}
return result;
} }

struts.xml配置

<package name="default" extends="json-default" namespace="/">
<!-- 配置拦截器和拦截器栈 -->
<interceptors>
<interceptor name="methodInterceptor" class="com.ytforever.interceptor.MethodInterceptor">
</interceptor>
<interceptor-stack name="methodStack">
<interceptor-ref name="methodInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 默认拦截器 -->
<default-interceptor-ref name="methodStack"></default-interceptor-ref>
</package>
<!-- 假如多个package,每个首行拷贝默认拦截器,不然,方法不被监听,无法记录 -->
        <!-- 默认拦截器 -->
        <default-interceptor-ref name="methodStack"></default-interceptor-ref>

2、数据库持久化记录,使用hibernate的EventListener事件监听

import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostUpdateEventListener;
/**
* HIBERNATE删除、修改监听
* @author ZhangLi
* @date 2018年8月28日 下午4:44:26
*/
public class UpdateDeleteListener implements PostInsertEventListener,PostUpdateEventListener, PostDeleteEventListener { private static final long serialVersionUID = 1L;
Logger logger = null;
ObjectMapper mapper = new ObjectMapper(); @Override
public void onPostDelete(PostDeleteEvent PD) {
try {
logger = Logger.getLogger(PD.getEntity().getClass()); StringBuffer sbInfo = new StringBuffer();
sbInfo.append("[ Data ] 删除数据 (Start)");
sbInfo.append("\n");
sbInfo.append(String.format("实体名:%s", PD.getEntity().getClass().getName()));
sbInfo.append("\n");
sbInfo.append(String.format("主键值:%s", PD.getId()));
sbInfo.append("\n");
sbInfo.append(String.format("表字段:%s", mapper.writeValueAsString(PD.getPersister().getPropertyNames())));
sbInfo.append("\n");
sbInfo.append(String.format("删数据:%s",mapper.writeValueAsString(PD.getDeletedState())));
sbInfo.append("\n");
sbInfo.append("[ Data ] 删除数据 (End)"); logger.warn(sbInfo);
} catch (Exception e) {
logger.error("[ Data ] 删除数据失败", e);
} } @Override
public void onPostUpdate(PostUpdateEvent PU) {
try {
logger = Logger.getLogger(PU.getEntity().getClass()); StringBuffer sbInfo = new StringBuffer();
sbInfo.append("[ Data ] 修改数据 (Start)");
sbInfo.append("\n");
sbInfo.append(String.format("实体名:%s", PU.getEntity().getClass().getName()));
sbInfo.append("\n");
sbInfo.append(String.format("修改前:%s",mapper.writeValueAsString(PU.getOldState())));
sbInfo.append("\n");
sbInfo.append(String.format("修改后:%s",mapper.writeValueAsString(PU.getEntity())));
sbInfo.append("\n");
sbInfo.append("[ Data ] 修改数据 (End)"); logger.warn(sbInfo);
} catch (Exception e) {
logger.error("[ Data ] 数据修改失败", e);
}
} @Override
public void onPostInsert(PostInsertEvent PI) {
try {
logger = Logger.getLogger(PI.getEntity().getClass()); StringBuffer sbInfo = new StringBuffer();
sbInfo.append("[ Data ] 新增数据 (Start)");
sbInfo.append("\n");
sbInfo.append(String.format("实体名:%s", PI.getEntity().getClass().getName()));
sbInfo.append("\n");
sbInfo.append(String.format("表字段:%s", mapper.writeValueAsString(PI.getPersister().getPropertyNames())));
sbInfo.append("\n");
sbInfo.append(String.format("新增的数据:%s",mapper.writeValueAsString(PI.getState())));
sbInfo.append("\n");
sbInfo.append("[ Data ] 新增数据 (End)"); logger.warn(sbInfo);
} catch (Exception e) {
logger.error("[ Data ] 新增数据失败", e);
}
} }

applicationContext.xml文件配置

<bean id="auditlogEvent" class="com.ytforever.interceptor.UpdateDeleteListener"></bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 设置Spring取那个包中查找相应的实体类 -->
<property name="packagesToScan">
<value>com.ytforever.bean,com.ytforever.workflow.bean</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<!-- 开发环境下需要,部署环境删除 -->
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="javax.persistence.validation.mode">none</prop>
<prop key="hibernate.jdbc.fetch_size">100</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry>
<key>
<value>post-insert</value>
</key>
<ref bean="auditlogEvent" />
</entry>
<entry>
<key>
<value>post-update</value>
</key>
<ref bean="auditlogEvent" />
</entry>
<entry>
<key>
<value>post-delete</value>
</key>
<ref bean="auditlogEvent" />
</entry>
</map>
</property>
</bean>

效果

小结

想完成系统访问记录、数据操作记录,重现操作过程。选用框架监听,完成记录,非常好使。

[沫沫金]JavaWeb企业信息系统,增加操作记录、数据库记录的更多相关文章

  1. 使用mybatis执行对应的SQL Mapper配置中的insert、update、delete等标签操作,数据库记录不变

    我使用springMVC集成mybatis,执行SQLMapper配置文件里的insert操作,发现程序没有报错,但数据库表里却没有刚才插入的记录.查了很多资料,终于在一篇博客上找到了答案:在执行完方 ...

  2. C#开发微信门户及应用(35)--微信支付之企业付款封装操作

    在前面几篇随笔,都是介绍微信支付及红包相关的内容,其实支付部分的内容还有很多,例如企业付款.公众号支付或刷卡支付.摇一摇红包.代金券等方面的内容,这些都是微信接口支持的内容,本篇继续微信支付这一主题, ...

  3. ajax基础语法、ajax做登录、ajax做用户名验证是否可用、ajax做关键字查询动态显示、ajax做用表格显示数据并增加操作列

    AJAX: AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新.   ...

  4. 传智播客JavaWeb day09-mysql入门、数据库操作、数据库表操作、数据行操作

    不知不觉已到了第九天了,今天主要讲了关系数据库的基本概述.安装.数据库.表和数据行的操作 1. 基本概述 1.1 数据库就是用来存储数据的.早期是存在文件里面的操作起来效率低而且不是很安全. 1.2 ...

  5. wcf 请考虑增加操作超时

    发送到 net.tcp://192.168.1.18:8732/CallbackService 的请求操作在配置的超时(00:00:59.9879994)内未收到回复. 分配给此操作的时间可能已经是更 ...

  6. MVC增加操作日志

    在后台管理中,有一些操作是需要增加操作日志的,尤其是对一些比较敏感的金额类的操作,比如商城类的修改商品金额.删除商品.赠送金额等人工的操作.日志中记录着相关操作人的操作信息,这样,出了问题也容易排查. ...

  7. 给Jquery easyui 的datagrid 每行增加操作链接(转)

    http://www.thinkphp.cn/code/207.html 通过formatter方法给Jquery easyui 的datagrid 每行增加操作链接我们都知道Jquery的EasyU ...

  8. JavaWeb response对象常用操作

      JavaWeb response对象常用操作 CreationTime--2018年7月18日10点42分 Author:Marydon 1.设置响应内容类型 方式一 response.setCo ...

  9. JavaWeb request对象常用操作

      JavaWeb request对象常用操作 CreateTime--2018年6月1日16点47分 Author:Marydon 一.前提 import javax.servlet.http.Ht ...

随机推荐

  1. for循环的3个参数

    1.最常用的方法是用来遍历集合 /** **第一个参数:表示循环的初始值,或初始条件,这里是i=0; **第二个参数:是循环的条件,这里是当i小于list的长度时; **第三个参数:每次循环要改变的操 ...

  2. Mahout实战---评估推荐程序

    推荐程序的一般评测标准有MAE(平均绝对误差),Precision(查准率),recall(查全率) 针对Mahout实战---运行第一个推荐引擎 的推荐程序,将使用上面三个标准分别测量 MAE(平均 ...

  3. Nodejs学习笔记(八)—Node.js + Express 实现上传文件功能(felixge/node-formidable)

    前言 前面讲了一个构建网站的示例,这次在此基础上再说说web的常规功能----文件上传,示例以一个上传图片的功能为例子 上传功能命名用formidable实现,示例很简单! PS:最近比较忙,距上一次 ...

  4. 01 JDBC的问题

    jdbc编程步骤: 1. 加载.注册数据库驱动   DriverManager 2. 创建并获取数据库链接   Connection 3. 创建jdbc statement/preparedState ...

  5. 基于VUE的SPA单页应用开发-加载性能篇

    1.基于异步数据的vue页面刷新 先看看基于异步数据的vue页面刷新后,都发生了啥- 如图所示: 图1 基于异步数据的vue页面刷新 网络请求图 步骤如下: step1:请求页面: step2:请求页 ...

  6. 本地开启https的nginx配置

    下载证书和key放置在nginx配置文件同级目录下,然后添加配置内容,监听443端口,如果本地443端口被占用,可以使用其他端口测试. server { listen 443; server_name ...

  7. [PY3]——logging

    logging模块的logger.handler.filter.formatter Logger记录器 提供日志接口,供应用代码使用.logger最长用的操作有两类:配置和发送日志消息.可以通过log ...

  8. How to update Ionic cli and libraries

    1)npm outdated 2)手动修改你项目的package.json文件,找对应的版本号 3)npm update 重新安装包就可以了. 转自:http://devfanaticblog.com ...

  9. Java Swing实战(一)JFrame和JTabbedPane容器

    概述: 项目是一个桌面程序,涉及标签和按钮组件.布局管理器组件.面板组件.列表框和下拉框组件等组件,以及Swing事件处理机制. 下面先从最基础的界面开始. /** * @author: lishua ...

  10. [js常用]文字转化成语音

    使用百度语音接口,实现文字转化成语音播放 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" &qu ...