普通操作日志和业务操作日志
在开始做之前,必须把两个日志分清楚,那就是普通操作日志和业务操作日志,这两者有何区别?
在我理解,普通操作日志就是单表的操作记录,而业务操作日志则就是一系列的普通操作日志的集合。
打个比方,用户需要购买一样宝贝,已经到了下单那步,下单就是个业务,这个业务背后就是一系列的业务,如:
生成订单 → 生成商品快照 → 发送一条站内信 → 删除购物车里对应宝贝
这样一个下单操作就包含了4部分,可以把这4部分看成是4张表,分别对这4张表进行对应的操作,就实现了业务。
因为不同项目的业务不尽相同,所以它无法做成通用模块,所以下面讲的是普通操作日志。
首先,哪些地方需要记录操作日志?执行insert、update、delete这3个操作的时候,就需要进行日志,而日志执行的先后顺序如下
insert | 在insert后执行 |
update | 在update前后都要执行,操作前获取操作前数据,操作后获取操作后数据 |
delete | 在delete前执行 |
实现的效果展示如下
根据界面可以抽象成2张表,一张主表一张从表,主表记录操作表及操作人等信息,从表记录操作的表字段信息。
下面说说实现
日志的管理可以使用过滤器,或者是Spring的拦截器进行日志的处理。
他们的好处,就是配一下就能够切入到系统当中。
如果是用过滤器,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用,然后进行日志记录。使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志。
另外一种就是使用Spring的AOP了,实现简单,只要配置一下配置文件就可以了。但这种方式会拦截下所有的对action或者dao的每个操作,所以效率比较低。
下面我说说使用AOP实现方式
在spring中配置。配置在指定包下的指定方法执行前或者后,执行指定类
<!-- 配置日志管理 -->
<bean id="LogManagerment"class="com.log.LogManagerment"></bean>
<aop:config>
<!--调用日志类-->
<aop:aspectid="b" ref="LogManagerment">
<!--配置在com.dao包下所有的类的add方法,在调用之前都会被拦截-->
<aop:pointcutid="logScope" expression="execution(*com.dao.*.add*(..))"/>
<!--在com.dao包下所有的类的add方法执行之前会调用LogManagerment中的before方法-->
<aop:beforepointcut-ref="logScope" method="before"/>
</aop:aspect>
</aop:config>
日志管理类,和配置文件对应:指定包下的指定方法执行前或者后,执行该类
LogManagerment的before方法,在com.dao包下所有的类的add方法执行之前会执行这个方法。
这个方法,通过反射得到增加实体的信息,包括实体名称、字段类型、字段值
/**
* 日志管理类
* @author TCH
*
*/
public class LogManagerment {
/**
* add操作执行之前执行的方法
* @param joinpoint包含action所有的相关配置信息和request等内置对象。
* @throwsClassNotFoundException
* @throwsInstantiationException
* @throwsIllegalAccessException
*/
public void before(JoinPoint joinpoint)
throwsClassNotFoundException, InstantiationException, IllegalAccessException{
ModelClassHelpermodelHelper = new ModelClassHelper();
//此方法返回的是一个数组,数组中包括request以及ActionCofig等类对象
joinpoint.getArgs();
Object[]argumnets = joinpoint.getArgs();
for (int i = 0;i < argumnets.length; i++) {
// 打印实体类
String className =argumnets[i].toString(); // com.entity.User@eiid000
String modelName =className.substring(className.lastIndexOf('.') + 1, className.indexOf('@'));
System.out.println("实体类:"+ modelName);
// System.out.println("实体对应表:t_"+ modelName.toLowerCase());
System.out.println("==========================");
// 打印参数对象信息
Object obj =argumnets[i];
List fieldInfos= modelHelper.getFiledsInfo(obj);
for(Iterator iter = fieldInfos.iterator(); iter
.hasNext();) {
Map map = (Map) iter.next();
System.out.println("字段类型:" + map.get("type"));
System.out.println("字段名称:" + map.get("name"));
System.out.println("字段值:" + map.get("value"));
System.out.println("==========================");
}
}
// 打印操作类型
StringmethodName = joinpoint.getSignature().getName();
System.out.println("方法名:" + methodName);
System.out.println("操作类型" +modelHelper.getOperationType(methodName));
System.out.println("被拦截方法调用之前调用此方法 before");
}
/**
* 根据方法名称,映射操作类型
* @param methodName
* @return
*/
private String getOperationType(String methodName) {
String type = "查询";
if (methodName.indexOf("add") > -1) {
type = "增加";
} else if (methodName.indexOf("del") > -1) {
type = "删除";
} else if (methodName.indexOf("update") > -1) {
type = "更新";
}
return type;
}
ModelClassHelper类,主要实现通过反射Object,得到对应的具体类信息
package com.log;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ModelClassHelper {
/**
* 通过反射Object,根据字段名称,获取字段值
* @param fieldName
* @param o
* @return
*/
private Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter +fieldName.substring(1);
Method method = o.getClass().getMethod(getter, new Class[] {});
Object value = method.invoke(o, new Object[] {});
return value;
} catch (Exception e) {
return null;
}
}
/**
* 通过反射Object,得到字段名称
* @param o
* @return
*/
private String[] getFiledName(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i].getType());
fieldNames[i] = fields[i].getName();
}
return fieldNames;
}
/**
* 通过反射Object,获取对象信息:类型、名称、值
* @param o
* @return
*/
public List getFiledsInfo(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
List list = new ArrayList();
Map infoMap = null;
for (int i = 0; i < fields.length; i++) {
infoMap = new HashMap();
infoMap.put("type", fields[i].getType().toString());
infoMap.put("name", fields[i].getName());
infoMap.put("value",getFieldValueByName(fields[i].getName(), o));
list.add(infoMap);
}
return list;
}
}
打印信息
实体类:User
==========================
字段类型:class java.lang.String
字段名称:username
字段值:admin
==========================
字段类型:class java.lang.String
字段名称:password
字段值:admin
==========================
字段类型:class java.lang.String
字段名称:name
字段值:tch
==========================
字段类型:int
字段名称:age
字段值:25
==========================
方法名:addUser
操作类型增加
被拦截方法调用之前调用此方法 before
这些打印的信息,在实际系统中应该写入数据库。这里有个小问题,通过aop可以得到实体类以及数据信息,这些信息都是通过映射得到的,跟具体业务无关,所以他们对应的名字(表名、字段名)怎么获得,我现在想的是把表的信息在配置文件中保留一份。
小结
我们容易着重系统的功能性实现,却忽视日志的作用。日志有系统日志、业务日志、工作日志等。
系统日志可供用户查询指定时间段内对系统的操作记录,也就是上面所说的普通操作日志,正如上面实现那样,跟具体业务无关,像个切片写入系统中,需要时配置下就好了。
业务操作日志则是跟系统的具体业务有关,是一系列的普通操作日志的集合。
工作日志在今目标中可以看到,用来跟踪业务的进展,方便管理者更好的把握工作进展情况,以便及时加以控制,有效调配资源。在今目标里,管理者可以批量查看一个部门或者项目团队成员的日志,跟踪重要事件,及时指导。工作日志还有一个业绩证明的作用,员工的工作量、工作效果在这里得到体现。工作日志,对管理很有帮助的工具。