应用程序日志管理!

来源:互联网 发布:小米air 装linux 编辑:程序博客网 时间:2024/06/07 05:15

如何做应用程序日志管理!


在一个事务处理系统中,有各种操作权限的操作人员从事其权限范围内的操作。事务处理日志要记载这些操作的全部历史记录,它对监测系统运行状态,为事后监督提供直接依据,对防范系统安全有重要作用。  
        讨论如何设计出满足事务处理基本特征的日志管理工具。基本设计思想是:定义日志管理的范围和内容,将日志管理设计成灵活的可重新定义方式,系统需要将哪些操作记入日志,完全由系统运行期间的系统管理设定,而不是在程序编制过程中事先设定好而不能整改。这种设计方法有两个优点是明显的。一是在程序开发其间,开发小组的编程人员并不需要每个人对自己设计的功能模块都编写日志处理子模块,而只是将向编写日志处理模块的编程人员说明哪些业务操作需要记入日志,并选择日志应填写哪些具体内容,这将大大减少编程代码,降低开发的工作量,提高开发效率,同时程序的模块化程度更高。二是系统投入运行后,系统的高级维护人员可以按照实际工作的需要来决定全部选择或部分选择系统可以已设定可以记入日志的操作事件,更具灵活性。  
        1、日志管理的数据库设计  
            日志管理的对象是进入系统的业务操作人员的操作事件。在开发环境下,这些操作反映在应用程序中可以抽象为以下几种操作:  
        1)点击某一个菜单项   
        2)进入了某个窗口  
        3)点击了某个窗口中的控件,如,按钮、下拉框、单选按钮、多选按钮、单行编辑器   等等,  
        4)向窗口控件中输入了新内容 如在datawindow   中进行了插入、删除、更新、检索等操作,在编辑掩码器中输入了新值等   
        5)退出了某个窗口  

          这些操作最终抽象成为对某个父对象的某个对象进行了某项操作,以此在mysql数据库中建立日志事件定义表kz_rz_dat,它定义了需要记录日志的操作,其库结构为:  
          rz_xh     integer     序号  
          dongz     integer     动作    1,进入窗口   
                                         2,退出窗口   
                                         3,点击了某个窗口中的控件  
                               4,向某些窗口控件输入了新内容  
                               5,点击某一个菜单项   
          czdx       string       操作对象名 可以是进入或退出的窗口名、在窗口中点击的控件的控件名、所点击菜单项的菜单名、或者是改变了属性或内容的窗口对象或控件的名称  
          fdx         string       操作对象的父对象名 
 
  日志表dj_rz_log记录操作操作的内容,如:操作的日期时间、操作员、操作所在的计算机名、IP地址或MAC地址、操作动作、操作对象名、操作对象的父对象名等。
  
  字段名  数据类型    说     明  
  Rzxh   long   日志序号  
  Riqi   datetime  日期和时间  
  Czyh   C6   操作员代号  
  Czyxm  C10   操作员姓名  
  Jqm   C10   机器名  
  Macdz  C18   MAC   地址  
  Ipdz   C12   Ip地址  
  Czdx   C40   操作对象,对象为窗口时,其值为窗口名   对象为窗口控件时,其值为控件名   对象为菜单时,其值为菜单名  
  Dongz  Integer  操作动作,   1-进入2-退出   3-点击 4-更改控件内容  
  dxxsmc  C40   对象显示名称,对象为窗口时,其值为this.title,对象为控件时,其值为this.text,对象为菜单时其值为this.text  
  fdx   C40   操作对象的父对象名,   操作对象为菜单和窗口时,其值可为空   操作对象为窗口控件时,其值为所在窗口  
   
          2、修改底层用户对象的相关事件  
          在应用程序,如果所有对象如菜单、窗口、窗口控件都通过继承上述对象而生成,我们就可以只添加或改这些对象的事件和函数,就能使所有继承对象都获得这些函数和事件。应用在日志管理上,如果在w_sheet的open事件中加入一段脚本,通过检索kz_rz_dat表,判断打开本窗口这一动作是否要记录日志,若需要记录,将相关内容记入dj_rz_log表。这样,所有从w_sheet继续而来的窗口具有了打开窗口的日志管理功能。依此类推,在窗口w_sheet的close事件中加入脚本,所有从w_sheet继续而来的窗口具有了关闭窗口的日志管理功能,在菜单m_menu的click事件中加入脚本,所有从m_nemu继续而来的菜单都有日志管理功能,   在可视用户对象u_cb的click事件中加入脚本,所有从u_cb继续而来的按钮具有了点击按钮的日志管理功能。等等。  
            3、日志管理功能  
          日志管理功能包括两个方面,一是定义需要记录日志的操作,二是对记录的日志按时间、操作员、操作动作进行查询、打印,以及定期清空日志等日志维护工作。这些操作都应该由系统管理员完成。  
            1)定义日志记录事件  
          定义日志记录事件实际上是对kz_rz_dat表的维护。其功能为增加、删除、查询日志记录事件项。为此,要建立一张应用程序功能与应用程序中用户对象名的对照表,系统管理员通过选择选项来确定需要记录日志的事件。如图:  
          数据窗口中显示了已定义的日志记录操作事件,如果要删除某一事件,可以在数据窗口中选中该行,点击删除钮即可。增加某一事件,可以先选择操作下拉框,选择操作动作,在对象下拉框中选择本操作的对象,(如动作为“打开”,则在对象下拉框中提供了所有可以打开的窗口),再根据本对象确定其父对象。选择了这三项后点击增加钮,即可将此事件加入日志记录定义表中,以后,此操作将要记录操作日志。  
            2)日志维护  
          日志维护功能主要是检索、打印和清空日志。这里我们提供了按时间、操作员姓名、操作机器的IP地址、操作动作排序进行检索,并可将其打印以备存档。在日志增长是一定长度时可以将其清空。  
   
   
   
  大家有什么看法尽管说。

---------------------------------------------------------------
系统日志管理解决方案

1 系统日志管理的作用
当系统操作员对系统中的数据进行新增、修改、作废等操作时,能够记录下相应的操作员、操作时间、操作员所用机器的IP、操作类型、操作对象等信息。如:在商位信息管理中,当操作员对某个商位进行修改后,要记录下上述提到的要素。

2 系统日志管理的设计
使用Spring2.0对AspectJ的支持实现。具体在一个java Bean 中用AspectJ标签实现切面,切面负责完成对系统日志管理要处理的信息进行组装、存储到表中,以便系统管理员进行查询;在切面中所需的操作员信息、操作员所用机器的IP通过在系统登录时存储到会话中,操作类型和操作对象信息应该通过AspectJ的连接点 进行获取。创建好java Bean 后,在spring 配置文件中加入<aop:aspectj-autoproxy/>,在系统加载时,系统会在已注入的Bean 中寻找包含 AspectJ标签的Bean。当触发Bean 中定义的切点后,将会执行切点对应的通知。相应的库表设计如下:

 

2.1 创建切面
切面是一个抽象的概念,从软件的角度来说是指在应用程序不同模块中的某一个领域或方面。从程序抽象的角度来说,可以对照OOP中的类来理解。OOP中的类(class)是实现世界模板的一个抽象,其包括方法、属性、实现的接口、继承等。而AOP中的切面(aspect)是实现世界领域问题的抽象,他除了包括属性、方法以外,同时切面中还包括切入点Pointcut、增强(advice)等,另外切面中还可以给一个现存的类添加属性、构造函数,指定一个类实现某一个接口、继承某一个类等。

在AspectJ5中,增加对了Java5注解的完全支持,可以使用Java注解来取代专门的AOP语法,把普通的Java类(POJO)声明为切面模块。Spring2提供了对AspectJ的支持,引用了AspectJ中的一个库来做切入点表达式语言的解析。这里,我们可以像在AspectJ5中进行切面编程一样,直接在Java类中用相关的注解来标识切面模块中的各部分。

2.1.1 连接点
连接点即运用程序执行过程中需要插入切面模块的某一点。连接点主要强调的是一个具体的“点”概念。这个点可以是一个方法、一个属性、构造函数、类静态初始化块,甚至一条语句。比如 3.2.3例子中的joinPoint ,连接点就是指具体的某一个方法。使用JoinPoint可以得到当前连接点的相关信息,比如对于方法连接点来说,可以得到方法名,方法参数等等。

2.1.2 定义切入点(Pointcut)
切入点是用来表示在连接点的何处插入切面模块,也可以称为一组连接点在一定上下文环境中的集合。AspectJ中的切入点主要有两种,一种是最基本的原始切入点,另外一种是由基本切入点组合而成的切入点。原始切入点是对连接点在特定上下文的表述,通过连接点的关键字以及一定的格式来声明。

进一步完善LogAspect类中功能。首先加入切入点的定义,使用@Pointcut标签来定义,在@AspectJ语法,我们在一个void方法上使用@Pointcut注解,就像下面这样:

在LogAspect中加入了一个匹配com.hs.common.service包及下面所有包中,名称为insert开头的方法的切入点:
@Pointcut("execution(* com.hs.common.service..*.insert(..))")

public void insertPointcut() {

}

当然,这个切入点可以直接在配置文件中引用。

@Pointcut注解允许我们定义切入点表达式,而且必要时,还有被切入点绑定的参数的个数和类型。方法名被用作切入点的名称。

execution()
execution()是最常用的切点函数,其语法如下所示:

execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)

除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。与其直接讲解该方法的使用规则,还不如通过一个个具体的例子进行理解。下面,我们给出各种使用execution()函数实例。
1)通过方法签名定义切点
? execution(public * *(..))
匹配所有目标类的public方法,但不匹配SmartSeller和protected void showGoods()方法。第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;
? execution(* *To(..))
匹配目标类所有以To为后缀的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一个*代表返回类型,而*To代表任意以To为后缀的方法;
2)通过类定义切点
? execution(* com.baobaotao.Waiter.*(..))
匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个*代表返回任意类型,com.baobaotao.Waiter.*代表Waiter接口中的所有方法;
? execution(* com.baobaotao.Waiter+.*(..))
匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaughtyWaiter#joke()这两个不在Waiter接口中定义的方法。
3)通过类包定义切点
在类名模式串中,“.*”表示包下的所有类,而“..*”表示包、子孙包下的所有类。
? execution(* com.baobaotao.*(..))
匹配com.baobaotao包下所有类的所有方法;
? execution(* com.baobaotao..*(..))
匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;
? execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点。
4)通过方法入参定义切点
切点表达式中方法入参部分比较复杂,可以使用“*”和“ ..”通配符,其中“*”表示任意类型的参数,而“..”表示任意类型参数且参数个数不限。
? execution(* joke(String,int)))
匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);
? execution(* joke(String,*)))
匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(String s1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)则不匹配;
? execution(* joke(String,..)))
匹配目标类中的joke()方法,该方法第一个入参为String,后面可以有任意个入参且入参类型不限,如joke(String s1)、joke(String s1,String s2)和joke(String s1,double d2,String s3)都匹配。
? execution(* joke(Object+)))
匹配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(String s1)和joke(Client c)。如果我们定义的切点是execution(* joke(Object)),则只匹配joke(Object object)而不匹配joke(String cc)或joke(Client c)。
args()和@args()
args()函数的入参是类名,@args()函数的入参必须是注解类的类名。虽然args()允许在类名后使用+通配符后缀,但该通配符在此处没有意义:添加和不添加效果都一样。
1)args()
该函数接受一个类名,表示目标类方法入参对象按类型匹配于指定类时,切点匹配,如下面的例子:
args(com.baobaotao.Waiter)
表示运行时入参是Waiter类型的方法,它和execution(* *(com.baobaotao.Waiter))区别在于后者是针对类方法的签名而言的,而前者则针对运行时的入参类型而言。如args(com.baobaotao.Waiter)既匹配于addWaiter(Waiter waiter),也匹配于addNaiveWaiter(NaiveWaiter naiveWaiter),而execution(* *(com.baobaotao.Waiter))只匹配addWaiter(Waiter waiter)方法;实际上,args(com.baobaotao.Waiter)等价于execution(* *(com.baobaotao.Waiter+)),当然也等价于args(com.baobaotao.Waiter+)。
2)@args()
该函数接受一个注解类的类名,当方法的运行时入参对象标注发指定的注解时,方法匹配切点。这个切点函数的匹配规则不太容易理解,我们通过以下示意图对此进行详细讲解:

2.1.3 定义增强(Advice)实现
我们可以在切面中进一步定义增强(Advice)实现,即根据切入点表达通知,同样使用标签。AspectJ支持很多增强类型,但当前Spring只支持其中部分标签, 包括@Before 在连接点之前调用的通知。比如,记录方法调用即将发生的日志。、@AfterReturning 如果在连接点的方法正常返回时调用的通知。@AfterThrowing如果连接点的方法抛出一个特殊的异常时调用的通知、@After在连接点之后调用的通知,无论结果是什么。特别像Java中的finally。、@Around能够完全控制是否执行连接点的通知。比如,用来在事务中封装某个方法调用,或者记录方法的执行时间。等几种。日志管理部分采用@ AfterReturning,如下:

@AfterReturning("insertPointcut() or updatePointcut() or removePointcut()")

public void insertLogInfo(ProceedingJoinPoint joinPoint)

{

if(joinPoint.getTarget().getClass().toString().indexOf("SystemLog") > -1){

return;

}

SystemLog systemLog = new SystemLog();

if (null != sessionValue) {

systemLog.setUserId(sessionValue.getUser().getAccount());

systemLog.setIpAdd(userIpAddress);

}

systemLog.setOperType(joinPoint.getSignature().getName());

systemLog.setOperObjec(joinPoint.getTarget().getClass().toString());

systemLog.setSlogComment("");

systemLog.setSlogType("");

entityService.insert(systemLog);

}

}
@ AfterReturning标签中的参数也就是前面我们定义的切入点;其后的方法即是增强的实现代码!将三个切入点加到一个表达式中对切入点应用通知,AND意味着两个切入点都必须应用。OR意味着必须应用其中一个切入点。我们可以构建我们需要的任何复杂程度的表达式。

上例中joinPoint.getSignature().getName() 为从连接点取得操作方法的名称,joinPoint.getTarget().getClass() 为从连接点取得对应的类名,记录操作该日志的用户名和IP是在用户登录时,存入会话信息中的,这里从会话中读取相应的信息,写入日志信息中。实现了对操作人,操作人对应的IP,操作动作,操作对象,操作时间等的记录。

entityService.insert(systemLog)方法将已获取的数据保存到系统日志表(YW_SYSTEMLOG)中,从而记录日志。

2.2 在spring配置文件中使用切面
要开启使用@AspectJ标签功能,需要在Spring配置文件中使用<aop:aspectj-autoproxy/>来开启在POJO中通过注解来标识切面模块的识别功能。这时要使用一个切面,只需把带有@AspectJ标签的Java类配置成一个普通Bean即可。如下面的例子:

<bean id="logAspect" class="com.hs.common.sys.LogAspect" />对应的java类LogAspect.java源码为:

package com.hs.common.sys;

import org.aspectj.lang.annotation.Aspect;

@Aspect

public class LogAspect {

}

@Aspect 切面,就像所有的AspectJ 切面,可以包含任意数目的切入点和通知方法。如下面是使用LogAspect的一个完整的Spring配置文件内容:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"

default-autowire="byName" default-lazy-init="true">

<aop:aspectj-autoproxy/>

<bean id="logAspect" class="com.hs.common.sys.LogAspect" />

<beanid="systemLogService" class="com.hs.common.service.implement.SystemLogServiceImp" />

</beans>

 通过这个配置,就会在指定的连接点执行完成后,插入书写日志的代码。注意在<beans>中要加入“xmlns:aop”命名声明,并在“xsi:schemaLocation”中指定aop配置的schema地址。

为了能正确运行客户端代码,需要把Spring项目lib目录下aspectj目录中的aspectjweaver.jar文件添加到classpath中。

2.3 系统日志操作查看
主页面显示如下:

.....

u 操作类型

下拉框,包括“新增”、“修改”、“删除”。

点击“查询”按钮根据查询条件查询系统日志表(YW_SYSTEMLOG)中记录的系统日志。

3 系统日志管理的功能
需求是希望能监控业务流水,比如对某个商位号的修改,能具体到记录下该商位号,目前的实现只能具体到该修改动作所在的类,以及修改动作对应的函数,尚不能取得该函数对应的参数值。因为能取得参数名,考虑用反射实现,但不同的动作对应的参数类型不同,尚不能统一处理。 


Tags: 系统  日志  管理  解决方案  Spring  java  IOC  aop 

原创文章如转载,请注明:转载自:飞扬部落编程仓库 : http://www.busfly.cn/csdn/

本文链接地址:http://www.busfly.cn/csdn/post/691.html

如果你喜欢本文,请顶一下,支持我,你的支持是我继续发好文章的最大动力。谢谢。
好东西需要分享,快把本文发给你的朋友吧~!~

=-------------------------------------------------
利用spring aop对日志进行管理,还是采用对比的方式进行,
修改前:

偶们的做法是在action里记录日志,注意这个日志是面向用户的日志,姑且称它为业务日志,至于后台日志,则在此文章中暂不考虑,基本是通过log4j打印到后台日志文件中。看下面一段代码:

 


 

   1.  try ...{  
   2.     employeInfoManageService.saveEmploye(vo, authForm.getLoginName());  
   3.  
   4.     LogVO logVO = new LogVO(Constants.LOG_LEVEL_INFO,  
   5.                 Constants.LOG_TYPE_BACK, "用户:" + authForm.getLoginName()  
   6.                             + "增加员工成功!");  
   7.     logService.saveLog(logVO);  
   8. } catch (Exception e) ...{  
   9.     log.error(e);  
  10.     LogVO logVO = new LogVO(Constants.LOG_LEVEL_ERROR,  
  11.             Constants.LOG_TYPE_BACK, "用户:" + authForm.getLoginName()  
  12.                             + "增加员工失败!");  
  13.     try ...{  
  14.         logService.saveLog(logVO);  
  15.     } catch (Exception e1) ...{  
  16.         log.error(e1);    
  17.     return messageForward("error", "alert.db.exception",     
  18.                     new Object[] ...{});   }  
  19. } 

 

这段代码实际上已经将写日志的过程封装起来,开发者只需要传入3个参数:操作者、是前台系统还是后台系统、以及日志的错误等级,其它的如操作者机器IP, 日志时间等信息由系统统一设定,即使是这样,如果一个action里面有多个操作,代码看起来也非常臃肿,而且给开发者增加了工作量,既然有了aop,为 什么不利用一下?看下面改造的代码:

 

   1.  LogVO logVO = new LogVO(Constants.LOG_LEVEL_INFO,  
   2.                 Constants.LOG_TYPE_BACK, "用户:" + authForm.getLoginName()  
   3.                             + "增加员工");  
   4. try ...{  
   5.     employeInfoManageService.saveEmploye(vo, authForm.getLoginName(), logVO);  
   6. } catch (Exception e) ...{  
   7.     log.error(e);  
   8.     return messageForward("error", "alert.db.exception",  
   9.                     new Object[] ...{});  
  10. } 
既然是应用到aop,当然少不了aop的配置了,看下面的配置代码:
 

 

   1.  <aop:config> 
   2.     <aop:advisor pointcut="execution(* *..*Service.*(..))" advice-ref="txAdvice"/> 
     3.     <aop:advisor pointcut="execution(* *..*Service.save*(..)) || execution(* *..*Service.update*(..)) || execution(* *..*Service.delete*(..))" advice-ref="logAfterAdvice"/> 
   4. </aop:config> 
   5.  
   6. <bean id="logAfterAdvice" class="com.fudannet.framework.aop.LogAfterAdvice"/> 
噢,aop:config的第一行是不是很熟悉啊,对,就是我们前面所配置的事务管理,这里,应该很清楚采用统一的aop配置的好处了吧。下面贴出logAfterAdvice的代码:
 

 

   1.  public void afterReturning(Object returnObj, Method method, Object[] args,  
   2.             Object targetObj) throws Throwable ...{  
   3.     if(method.getName().equals("saveLog")) return;  
   4.     for(int i = 0; i < args.length; i++)...{  
   5.         if(args[i] instanceof LogVO)...{  
   6.             log.info("开始写入日志......");  
   7.             writeLog((LogVO)args[i]);  
   8.         }  
   9.     }  
  10. }  
  11.  
  12. private void writeLog(LogVO vo)...{  
  13.     try ...{  
  14.         vo.setDescription(vo.getDescription() + "成功!");  
  15.         logService.saveLog(vo);  
  16.     } catch (RuntimeException e) ...{  
  17.         log.error(e);  
  18. }  
  19.  
  20. public void setLogService(LogService logService) ...{  
  21.     this.logService = logService;  
  22. } 


 

这段代码应该很清楚了,将logService注入到拦截log的advice里,进行正确操作的日志记录,而afterReturning方法里 的第一行判断是由于logService里的写日志的方法是以save开始的。所以,如果拦截器拦截到此方法,不需要记录日志。

正确的日志记录完,当然如果发生异常,我们需要记录操作的失败日志,当然了,我们也是通过aop来做,但是这次是通过实现exception advice来实现,代码如下:


   1.  public void afterThrowing(Method method,Object[] args,Object target,Exception e) throws Throwable ...{  
   2.     if(method.getName().equals("saveLog")) return;  
   3.     for(int i = 0; i < args.length; i++)...{  
   4.         if(args[i] instanceof LogVO)...{  
   5.             log.info开始写入日志......"); 
   6.             writeLog((LogVO)args[i]); 
   7.         } 
   8.     } 
   9. } 
  10. 
  11. private void writeLog(LogVO vo)...{ 
  12.     try ...{ 
  13.         vo.setDescription(vo.getDescription() + "失败!");  
  14.         logThrowService.saveLog(vo);  
  15.     } catch (RuntimeException e) ...{  
  16.         log.error(e);  
  17.     }  
  18. }  
  19.  
  20. public void setLogThrowService(LogService logThrowService) ...{  
  21.     this.logThrowService = logThrowService;  
  22. } 

 

上面代码已经很好的说明了,如果发生exception的话,日志是怎么记录的,这里要提到的一点的是,异常的处理稍微有一些复杂,就拿本例的代码能看出 来,只要在service层有异常的时候,都会记录失败日志,实际上,很多时候,未必是这样,在某个模块,可能需要定义一种特殊的异常,而一旦这种异常发 生,则需要进入另外一个流程或者做一些特殊的处理,这个时候需要根据具体情况做一些变更,比如在上面代码我们加上:
   1.  public void afterThrowing(Method method,Object[] args,Object target,OrderException e) throws Throwable ...{  
   2.     log.info("......");  
   3.     //do something  
   4. } 
则如果OrderException被抛出,就会到此方法中执行,而不会去写日志。
--------------------------------------------------------------------------------
其中的封装方式虽然一般,但是还是比较好的解决了问题!对于LogVO类我们可以把他继续扩展下去,比如设定访问用户,IP,seq文等.

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1621513

 

原创粉丝点击