使用spring aop结合log4j做日志

整理一下逻辑,一个类中的一个方法被调用时,AOP配置文件会去检查你是否对此方法配置了日志切面,如果配置了,这儿是后向切入,他会在
执行此方法之后执行切入类的一个方法(AOP配置此方法),切入类得到了此方法的完整路径名称,他会到一个配置文件中去读取方法描述,然后用log4j记录此描述信息,后面的工作就交给log4j了,log4j会把信息从屏幕输出,并记录到一个日志文件,位一WEB-INF/log下,还会把数据插入到数据库,但是还需要用户信息,不过Filter已经为我准备好了,系统可以记录一条日志信息类似:


2010-01-01 17:34   用户dreamsover[id号]添加了审核信息


这是log4j的配置文件,定义了3个输出域,此文件位于WEB-INF/conif/log4j(因为我定义了一个输出域为文件,而且要存储到本项目的WEB-INF子目录下,要用到${webapp.root}变量,把log4j配置文件放到WEB-INF下最好,在系统启动时用spring加载,下面贴上web.xml中的配置,包括加载此文件)

ERROR>INFO>DEBUG
log4j.properties  
log4j.rootLogger=INFO,stdout,logfile,db
            
#spring log
log4j.logger.org.springframework=ERROR


#Console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}   %X{id},%X{name},%m %C%l%n

#Filelogfile
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=${webapp.root.webtools}/WEB-INF/logs/system.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}   %X{name},%m %n

#JDBC Appender
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.db.BufferSize=2
log4j.appender.db.driver=sun.jdbc.odbc.JdbcOdbcDriver
log4j.appender.db.URL=jdbcdbc:report
log4j.appender.db.user=loveus
log4j.appender.db.password=123456
log4j.appender.db.sql=insert into LOG (USERID,NAME,[TIME],INFO) values ('%X{id}','%X{name}','%d{yyyy-MM-dd HH\:mm\:ss}','%m')

web.xml配置文件,此文件加载了struts,spring,并用spring加载了log4j,注意里面配置了一个Filter : GetUserFilter ,他的作用是从SESSION中获取当前登录用户的信息,在Log4j配置文件中用到了, 比如%X{name}是获取用户姓名,但是要在此Filter中先设置name属性。GetUserFilter是关键所在,因为等一下用AOP切入的方法已经得不到用户信息了,就要靠他了。GetUserFilter我会在最后贴出来。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<context-param>
<param-name>webAppRootKey</param-name>
<param-value>webapp.root.webtools</param-value>
</context-param>

<!-- Spring ApplicationContext配置文件的路径,可使用通配符。多个路径用逗号分隔。此参数用于后面的Spring-Context loader -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/conif/spring/applicationContext-*.xml</param-value>
</context-param>

   <!--由Spring载入的Log4j配置文件位置-->
   <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/conif/log4j/log4j.properties</param-value>
</context-param>

   <!--Spring默认刷新Log4j配置文件的间隔,单位为millisecond-->
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value>
</context-param>

<!-- Log4jConfigListener会去log4j.propeties 读取配置文件;开一条watchdog线程每60秒扫描一下配置文件的变化 -->
<listener>
    <listener-class>
      org.springframework.web.util.Log4jConfigListener
    </listener-class>
</listener>

<!-- 根据spring的配置文件加载spring -->
<listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<!-- Spring 刷新Introspector防止内存泄露 -->
<listener>
    <listener-class>
      org.springframework.web.util.IntrospectorCleanupListener
    </listener-class>
</listener>

   <servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
   <param-name>config</param-name>
   <param-value>/WEB-INF/conif/struts/struts-config.xml</param-value>
</init-param>
<init-param>
   <param-name>debug</param-name>
   <param-value>3</param-value>
</init-param>
<init-param>
   <param-name>detail</param-name>
   <param-value>3</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
   </servlet>

   <servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
   </servlet-mapping>
   <welcome-file-list>
<welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   <filter>
<filter-name>Filter</filter-name>
<filter-class>util.Filter</filter-class>
   </filter>
   <filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/*</url-pattern>
   </filter-mapping>

<filter>
<filter-name>GetUserFilter</filter-name>
<filter-class>util.GetUserFilter</filter-class>
   </filter>
   <filter-mapping>
<filter-name>GetUserFilter</filter-name>
<url-pattern>/*</url-pattern>
   </filter-mapping>

  
   <error-page>
<error-code>404</error-code>
<location>/jsp/error/404.jsp</location>
   </error-page>

</web-app>

AOP的切面类,他是一个普通类,这个类很简单,这么多代码其实就是为了读取一个配置文件WEB-INF/conif/log4j/Log.properties, 这个文件中配置了方法的具体描述信息:
com.dreamsover.report.assessManage.bs.impl.AssessBsImpl.addAssess=\u6DFB\u52A0\u6BCF\u6708\u5BA1\u6838\u4FE1\u606F
com.dreamsover.report.assessManage.bs.impl.AssessBsImpl.getLineAssesssThisMonth=\u67E5\u8BE2\u672C\u6708\u6240\u6709\u5BA1\u6838\u60C5\u51B5
com.dreamsover.report.checkManage.bs.impl.CheckBsImpl.addCheck=\u6DFB\u52A0\u672C\u6708\u68C0\u67E5\u62A5\u8868


系统记录的就是这些描述信息,AOP用到的方法是addLog,这个方法读取相应的方法名(com.dreamsover.report.assessManage.bs.impl.AssessBsImpl.addAssess),从配置文件得到描述信息,然后用log4j记录。稍候贴出AOP的配置。

/**
* 文件名: addLog.java
* 描述: TODO(日志AOP切面类)
* 修改人: dreamsover
* 修改时间: 2010-1-19
* 修改内容:创建类
* @author: lio
* @date: 2010-1-19
* @version V1.0
*/

package util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;

import com.dreamsover.report.systemManage.vo.Employee;

/**
* 类名:addLog
* 
* @author: lio
* @version 1.0
*/
public class AddLog {

// 定义LOG4J对象
static Logger logger = Logger.getLogger(AddLog.class.getName());

// 日志配置文件
static Properties properties = null;
// 读取日志配置文件
static {
properties = new Properties();

try {

FileInputStream in = new FileInputStream("/" + getWEBINFAddress()
+ File.separator + "conif/log4j/Log.properties");

properties.load(in);
} catch (FileNotFoundException e) {
logger.error("没有找到日志配置文件,请确认你的路径是否正确。");
} catch (IOException e) {
logger.error("日志配置文件读写错误");
}
}

/**
    * 描述: 记录系统安全日志的方法
    * 
    * @author: lio
    * @param joinPoint
    * @version: 1.0
    */
@SuppressWarnings("unused")
private void addLog(JoinPoint joinPoint) {

String key = joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName();
// 得到方法描述信息
String info = properties.getProperty(key);
if (info != null && !info.equals("") && !info.equals(" ")) {
logger.info(info);
} else {
/* System.out.println("请检查您的日志配置文件,AOP" +
"中配置了此方法记录日志,但是没有在配置文件中找到该方法的描述,方法名:" + key);
*/ 
}

}


/**
    * 处理路径
    * @return
    */
public static String getWEBINFAddress() {
Class theClass = AddLog.class;
java.net.URL u = theClass.getResource("");
// str会得到这个函数所在类的路径
String str = u.toString();
// 截去一些前面6个无用的字符
str = str.substring(6, str.length());
// 将%20换成空格(如果文件夹的名称带有空格的话,会在取得的字符串上变成%20)
str = str.replaceAll("%20", " ");
// 查找“WEB-INF”在该字符串的位置
int num = str.indexOf("WEB-INF");
// 截取即可
str = str.substring(0, num + "WEB-INF".length());
return str;
}
}

AOP配置文件,AOP实现全靠他了,我就留了一个模块的配置,需要可以轻松的添加,但是记得在WEB-INF/conif/log4j/Log.properties文件中添加相应的描述信息

<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


<!-- 日志配置 -->


<bean id="logClass" class="util.AddLog" />
<!-- AOP配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<aop:config>
<aop:aspect id="log" ref="logClass">

                     <!-- 审核模块BS层 -->
<aop:pointcut expression="execution (* com.dreamsover.report.assessManage.bs..*.*(..))" id="logbsAssessPoint"/>
<aop:after method="addLog" pointcut-ref="logbsAssessPoint"/>

</aop:aspect>
</aop:config>

</beans>

至此,只差一个得到用户的Filter了,其中最重要的是MDC,在log4j中得到用户信息就靠他了
/**
* 文件名: GetUserFilter.java
* 描述: TODO(得到SESSION中的用户)
* 修改人: lio 
* 修改时间: 2010-1-20
* 修改内容:创建类
* @author: lio
* @date: 2010-1-20
* @version V1.0
*/

package util;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.MDC;

import com.dreamsover.report.systemManage.vo.Employee;

/**
* 类名:GetUserFilter
* 
* @author: dreamsover
* @version 1.0
*/

public class GetUserFilter implements Filter {

//定义默认用户姓名
private final static String DEFAULT_USER = "guest";

public void destroy() {
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session == null) {
MDC.put("id", (int) (Math.random() * 1000));
MDC.put("name", DEFAULT_USER);
MDC.put("username", DEFAULT_USER);
} else {
Employee user = (Employee) session.getAttribute("user");
if (user == null) {
MDC.put("id", (int) (Math.random() * 1000));
MDC.put("name", DEFAULT_USER);
MDC.put("username", DEFAULT_USER);
} else {
MDC.put("id", user.getId());
MDC.put("name", user.getName());
MDC.put("username", user.getUsername());
}
}

chain.doFilter(request, response);
}

public void init(FilterConfig Config) throws ServletException {

}
}

/**
* 文件名: GetUserFilter.java
* 描述: TODO(得到SESSION中的用户)
* 修改人: lio 
* 修改时间: 2010-1-20
* 修改内容:创建类
* @author: lio
* @date: 2010-1-20
* @version V1.0
*/

package util;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.MDC;

import com.dreamsover.report.systemManage.vo.Employee;

/**
* 类名:GetUserFilter
* 
* @author: dreamsover
* @version 1.0
*/

public class GetUserFilter implements Filter {

//定义默认用户姓名
private final static String DEFAULT_USER = "guest";

public void destroy() {
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session == null) {
MDC.put("id", (int) (Math.random() * 1000));
MDC.put("name", DEFAULT_USER);
MDC.put("username", DEFAULT_USER);
} else {
Employee user = (Employee) session.getAttribute("user");
if (user == null) {
MDC.put("id", (int) (Math.random() * 1000));
MDC.put("name", DEFAULT_USER);
MDC.put("username", DEFAULT_USER);
} else {
MDC.put("id", user.getId());
MDC.put("name", user.getName());
MDC.put("username", user.getUsername());
}
}

chain.doFilter(request, response);
}

public void init(FilterConfig Config) throws ServletException {

}
}


好了,所有代码已经完成了,再整理一下逻辑,一个类中的一个方法被调用时,AOP配置文件会去检查你是否对此方法配置了日志切面,如果配置了,这儿是后向切入,他会在执行此方法之后执行切入类的一个方法(AOP配置此方法),切入类得到了此方法的完整路径名称,他会到一个配置文件中去读取方法描述,然后用log4j记录此描述信息,后面的工作就交给log4j了,log4j会把信息从屏幕输出,并记录到一个日志文件,位一WEB-INF/log下,还会把数据插入到数据库,但是还需要用户信息,不过Filter已经为我准备好了,系统可以记录一条日志信息类似:


2010-01-01 17:34   用户dreamsover[id号]添加了审核信息


#JDBC Appender
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender 
log4j.appender.db.BufferSize=2 
log4j.appender.db.driver=com.mysql.jdbc.Driver
log4j.appender.db.URL=jdbc:mysql://localhost:3306/twioo_admin?useUnicode=true&characterEncoding=UTF-8
log4j.appender.db.user=root
log4j.appender.db.password=root
log4j.appender.db.sql = INSERT INTO twioo_log(logDATE,logTIME,LOGThread,loglevel,logClass,logLogger,logMessage) values ('%d{yyyy-MM-dd}','%d{HH:mm:ss}','%t', '%p', '%c', '%l', '%m') 
log4j.appender.db.layout=org.apache.log4j.PatternLayout
#JDBC Appender
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender 
log4j.appender.db.BufferSize=2 
log4j.appender.db.driver=com.mysql.jdbc.Driver
log4j.appender.db.URL=jdbc:mysql://localhost:3306/admin?useUnicode=true&characterEncoding=UTF-8
log4j.appender.db.user=root
log4j.appender.db.password=root
log4j.appender.db.sql = INSERT INTO log(logDATE,logTIME,LOGThread,loglevel,logClass,logLogger,logMessage) values ('%d{yyyy-MM-dd}','%d{HH:mm:ss}','%t', '%p', '%c', '%l', '%m') 
log4j.appender.db.layout=org.apache.log4j.PatternLayout
CREATE TABLE LOG (
        ID                 INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
        LOGDATE            DATE,
        LOGTIME            TIME,
        LOGTHREAD          VARCHAR(50),
        LOGLEVEL           VARCHAR(50),
        LOGCLASS           VARCHAR(200),
        LOGLOGGER          VARCHAR(200),
        LOGMESSAGE         VARCHAR(2000)
)
# send email
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=ERROR 
log4j.appender.MAIL.BufferSize=2

log4j.appender.MAIL.SMTPHost=xxx
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=xx
log4j.appender.MAIL.SMTPUsername=liangyong
log4j.appender.MAIL.SMTPPassword=xx
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

总结一下: 
--------------------------------------------------------------------------------

1,pointcut既可以定义在一个接口上面(表示实现该接口的类方法将被拦截),同时也可以定义在一个类上面(无接口的情
   况,需要强制使用cglib)。在接口上面定义pointcut时无需关心接口实现类的具体位置,只需要定义被拦截的接口及方
   法位置。

2,常见的情况:
x.y.service..*Service.*(..)
x.y.service —— 包“x.y.service”
x.y.service.. —— 包“x.y.service”及其子包例如:“x.y.service.abc”,“x.y.service.def”,“x.y.service.ghi”,“x.y.service.jkl”。。。
*Service —— 定义接口(或没有实现接口的类,需要使用cglib代理)表达式;所有以Service结尾的类或接口,注意不是所有以Service结尾的包名。
*(..) —— 定义方法名,方法参数表达式;任意方法的名称,任意方法参数。

com.xyz.service.*.*(..)
com.xyz.service —— 包“com.xyz.service”
*.*(..) —— 任意接口(或没有实现接口的类,需要使用cglib代理),任意方法,任意参数
在service包下定义的任意方法的执行。

com.xyz.service..*.*(..)
com.xyz.service —— 包“com.xyz.service”
com.xyz.service.. ——包“com.xyz.service”及其子包
*.*(..) —— 任意接口(或没有实现接口的类,需要使用cglib代理),任意方法,任意参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值