AOP实现日志管理

来源:互联网 发布:js date格式化 编辑:程序博客网 时间:2024/06/05 23:46

利用AOP实现在controller层实现日志记录,往日志文件里写相应日志信息。


先在log4j进行log功能的一些配置

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"threshold="all" debug="false"><appender name="console" class="org.apache.log4j.ConsoleAppender"><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%d %-5p %c %x - %m%n" /></layout><filter class="org.apache.log4j.varia.LevelRangeFilter"><param name="levelMin" value="DEBUG" /><param name="levelMax" value="FATAL" /><param name="AcceptOnMatch" value="true" /></filter></appender><appender name="file" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern" value="${webapp.root}logs/api/api-log-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %m%n" /></layout></appender><appender name="exceptionAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern" value="${webapp.root}logs/api/api-error-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %m%n" /></layout></appender>        <appender name="chargeAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern" value="${webapp.root}logs/api/api-charge-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %m%n" /></layout></appender><appender name="userBehaviorAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern"value="${webapp.root}logs/api/api-user-behavior-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%m%n" /></layout></appender><appender name="inOutPutAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern"value="${webapp.root}logs/api/api-inOutPut-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%m%n" /></layout></appender><appender name="httpAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern" value="${webapp.root}logs/api/api-http-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %m%n" /></layout></appender><appender name="accessAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern"value="${webapp.root}logs/api/api-access-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%m%n" /></layout></appender><appender name="serviceAppender" class="org.apache.log4j.rolling.RollingFileAppender"><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"><param name="FileNamePattern"value="${webapp.root}logs/api/api-servitization-%d{yyyyMMdd}.log" /></rollingPolicy><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %m%n" /></layout></appender><logger name="exceptionLog" additivity="false"><level value="INFO" /><appender-ref ref="exceptionAppender" /></logger><logger name="chargeLog" additivity="false"><level value="INFO" /><appender-ref ref="chargeAppender" /></logger><logger name="userBehaviorLog" additivity="false"><level value="INFO" /><appender-ref ref="userBehaviorAppender" /></logger><logger name="inOutPutLog" additivity="false"><level value="INFO" /><appender-ref ref="inOutPutAppender" /></logger><logger name="httpLog" additivity="false"><level value="INFO" /><appender-ref ref="httpAppender" /></logger><logger name="accessLog" additivity="false"><level value="INFO" /><appender-ref ref="accessAppender" /><!-- <appender-ref ref="accessAppenderScribe" /> --></logger><logger name="serviceLog" additivity="false"><level value="INFO" /><appender-ref ref="serviceAppender" /></logger><root><priority value="error" /><appender-ref ref="console" /><appender-ref ref="file" /></root></log4j:configuration>

再创建一个Java文件(LogHelper),作为工具类,往日志表里写入日志或输出到控制台等等。

package com.thc.warehouse.controller.api.log;import java.io.PrintWriter;import java.io.StringWriter;import java.net.URL;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;import java.util.StringTokenizer;import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang.StringUtils;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.log4j.Logger;import org.apache.log4j.xml.DOMConfigurator;import com.alibaba.fastjson.JSON;import com.thc.warehouse.controller.util.RegexUtil;/** * @author Meng */public class LogHelper {protected static final Log log = LogFactory.getLog(LogHelper.class);private static boolean isInit = false;private final Object lock = new Object();protected static Class LogHelper; /* synthetic field */private static String logXml = "/log4j.xml";private Logger logger;private static HashMap<String, LogHelper> logHelperMap = new HashMap<String, LogHelper>();/** * 初始化log4j配置文件 *  * @param logXmlConfigFile */public static void init(String logXmlConfigFile) {log.debug("init log");if ((logXmlConfigFile != null) && (!logXmlConfigFile.equals(""))) {logXml = logXmlConfigFile;}}/** * constructor init the Log4j only one time */private LogHelper() {synchronized (lock) {if (!isInit) {String file = null;try {// file = SystemConfig.getProperty("log.file.path");if (StringUtils.isBlank(file)) {URL res = super.getClass().getResource(logXml);if (res == null) {file = "." + logXml;} else {file = res.getPath();}}DOMConfigurator.configure(file);isInit = true;} catch (Exception e) {e.printStackTrace();log.error("Log4j init fail:" + e.toString());}}}}/** * get a LogHelper instance by the log name. *  * @param logName * @return */public static LogHelper getInstatnce(String logName) {if (!logHelperMap.containsKey(logName)) {LogHelper tmphelper = new LogHelper();tmphelper.logger = Logger.getLogger(logName);logHelperMap.put(logName, tmphelper);return tmphelper;}return logHelperMap.get(logName);}/** * for log4j ,the log level is info *  * @param str */public void write(String str) {logger.info(str);}public void trace(Exception e) {logger.info(LogHelper.class, e);}public void trace(Exception e, String str) {logger.info(str);logger.info(LogHelper.class, e);}public static String getLogClass() {return "";}/** * 输出到控制台 *  * @param str */public static void stdout(String str) {System.out.println(getLogDate() + getLogClass() + str);}public static void stderr(String str) {System.err.println(getLogDate() + getLogClass() + str);}public static void stderrTrace(Exception e) {System.err.println(getLogDate() + getLogClass() + e.toString());e.printStackTrace();}private static String getLogDate() {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return "[" + df.format(new Date()) + "] ";}public static void studioLog(String str) {log.info(str);getInstatnce("studioLog").logger.fatal(getLogClass() + str);}public static String locate(String esc) {StringWriter sw = new StringWriter();(new Exception()).printStackTrace(new PrintWriter(sw));StringTokenizer st = new StringTokenizer(sw.toString(), "\n");do {if (!st.hasMoreTokens()) {break;}String str = st.nextToken();if (str.indexOf("Exception") != -1 || str.indexOf((LogHelper != null ? LogHelper: (LogHelper = class$("com.ihandy.yuncai.studio.util.IhandyLogHelper"))).getName()) != -1 || esc != null && str.indexOf(esc) != -1) {continue;}if (esc == "!@*#~^?'/\"") {return str;}int i = str.indexOf(40);int j = str.indexOf(41);if (i != -1 && j != -1) {return str.substring(i, j + 1);}break;} while (true);return "";}protected static Class class$(String x1) {try {return Class.forName(x1);} catch (ClassNotFoundException x2) {throw new NoClassDefFoundError(x2.getMessage());}}public static void exceptionLog(Throwable t) {try {String str = getTrace(t);str = "\r\n>>>>>>>>>>>\r\n" + getLogDate() + "\r\n" + getLogClass() + str + "\r\n<<<<<<<<<";System.out.println(str);getInstatnce("exceptionLog").logger.fatal(str);} catch (Throwable e) {e.printStackTrace();}}public static void exceptionLog(String msg, Throwable t) {try {String str = getTrace(t);str = "\r\n>>>>>>>>>>>" + getLogDate() + "\r\n" + getLogClass() + "\r\n" + msg + "\r\n" + str + "\r\n<<<<<<<<<";System.out.println(str);getInstatnce("exceptionLog").logger.fatal(str);} catch (Throwable e) {e.printStackTrace();}}public static void exceptionLog(String description, String errorMsg) {try {String str = "\r\n>>>>>>>>>>>" + getLogDate() + "\r\n" + getLogClass() + "\r\n" + description + "\r\n" + errorMsg + "\r\n<<<<<<<<<";System.out.println(str);getInstatnce("exceptionLog").logger.fatal(str);} catch (Throwable e) {e.printStackTrace();}}public static void exceptionLog(String description) {exceptionLog(description, "");}public static String getTrace(Throwable t) {StringWriter stringWriter = new StringWriter();PrintWriter writer = new PrintWriter(stringWriter);t.printStackTrace(writer);StringBuffer buffer = stringWriter.getBuffer();return buffer.toString(); }/** * 访问日志参数添加方法,参数需要以键值对的形式传入 * 调用示例LogHelper.addAccessLogAttribute(request, "rechargeType", cardType, "rechargeAmount", amount.toString()); * @param request * @param paramNames 参数及值对 */public static void addAccessLogAttribute(HttpServletRequest request, Object... paramNames) {if ((paramNames.length % 2) == 0) { for (int i = 0; i < paramNames.length / 2; i++) {request.setAttribute(String.valueOf(paramNames[2 * i]), paramNames[2 * i + 1]);}} else {String msg = "向访问日志传递日志参数不成对,属性无法添加";exceptionLog(msg, new RuntimeException());}}/** * 请求参数 * @return */public static String requestParamsJson(HttpServletRequest req) {String paramsJson = "";Map<String, Object> map = new HashMap<String, Object>();Enumeration<String> paramNames = req.getParameterNames();while (paramNames.hasMoreElements()) {String paramName = paramNames.nextElement();//进行屏蔽,不记录到日志if (RegexUtil.validate("\\w*password\\w*", paramName)) {continue;}//oios1.30需求中,屏蔽掉vip验证和恢复接口的参数if ("jsondata".equals(paramName)) {continue;}String paramValue = req.getParameter(paramName);//参数的值超过500个字节时做截断if (paramValue.length() > 500) { paramValue = paramValue.substring(0, 500);}map.put(paramName, paramValue);}if (map != null && map.size() > 0) {paramsJson = JSON.toJSONString(map);//对“][”日志分割符进行转码成"]_["paramsJson = paramsJson.replaceAll("]\\[", "]_[");}return paramsJson;}/** * 处理参数 * @param request * @param paramNames */public static void addProcessingParams(HttpServletRequest request, Object... paramNames) {Map<String, Object> map = new HashMap<String, Object>();if ((paramNames.length % 2) == 0) {for (int i = 0; i < paramNames.length / 2; i++) {map.put(String.valueOf(paramNames[2 * i]), paramNames[2 * i + 1]);}} else {String msg = "处理参数:向访问日志传递日志参数不成对,属性无法添加";exceptionLog(msg, new RuntimeException());}request.setAttribute("processingParams", JSON.toJSONString(map));}}

接下来配置AOP-----------------------------------



(以下配置时注意路径问题)

第一种配置方法:使用@AspectJ标签

  1. 在配置文件中添加<aop:aspectj-autoproxy/>注解(注:SpringMVC的配置文件中完成,不要在spring的配置文件中完成
  2. 创建一个Java文件,使用@Aspect注解修饰该类(LogHandler)
  3. 创建一个方法,使用@Before、@After、@Around等进行修饰,在注解中写上切入点的表达式(LogHandler中的方法)

说明:上述Java文件创建好后,需要将其在Spring的容器中进行声明,可以在配置文件中定义<bean/>节点,也可以使用@Component组件进行修饰

1、在SpringMVC配置文件中添加AOP注解的相关配置信息

<!--注解实现AOP切面 -->
<!-- 启动对@AspectJ注解的支持,通知spring使用cglib而不是jdk的来生成代理方法  -->    <aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
<!--将Aspect类注册为Bean,@AfterReturning时需要使用此配置 -->
     <context:component-scan base-package="com.thc.warehouse.util"><context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>     </context:component-scan>
2、建一个文件进行相关的代理操作

package com.thc.warehouse.controller.util;import java.text.SimpleDateFormat;import java.util.Date;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;import com.thc.warehouse.controller.api.log.LogHelper;@Component//注解实现AOP切面@Aspect//注解实现AOP切面public class LogHandler {static{System.out.println("-----------已加载LogHandler-----------");//查看LogHandler是否加载}    //方法执行的前调用 @Before("execution(* com.thc.warehouse.controller.*.*.*(..))")//注解实现AOP切面      public void doBeforeInServiceLayer(JoinPoint joinPoint) {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(df.format(new Date())+"====================================@Before:doBeforeInServiceLayer====================================");        StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@Before:doBeforeInServiceLayer====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }    //方法执行的后调用 @After("execution(* com.thc.warehouse.controller.*.*.*(..))")//注解实现AOP切面      public void doAfterInServiceLayer(JoinPoint joinPoint) {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(df.format(new Date())+"====================================@After:doAfterInServiceLayer====================================");        StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@After:doAfterInServiceLayer====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }    //方法执行的前后调用 @Around("execution(* com.thc.warehouse.controller.*.*(..))")//注解实现AOP切面    public void doAround(ProceedingJoinPoint pjp) throws Throwable {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@Around:doAround====================================");System.out.println(df.format(new Date())+"====================================@Around:doAround====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }        //方法运行出现异常时调用      @AfterThrowing(pointcut = "execution (* com.thc.warehouse.controller.*.*(..))",throwing = "ex")//注解实现AOP切面    public void doAfterThrowing(Exception ex){      System.out.println("====================================@AfterThrowing:doAfterThrowing====================================");        System.out.println(ex);      }
/** * ControllerLayer * 方法执行的后返回值 * @param joinPoint * @param returnValue */@AfterReturning(pointcut = "execution (* com.thc.warehouse.controller.*.*.*(..))", returning = "returnValue")public void doAfterReturnInControllerLayer(JoinPoint joinPoint, Object returnValue) {if(logSwitch){SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(df.format(new Date())+" INFO "+joinPoint.getTarget().getClass().getName()+":"+joinPoint.getSignature().getName()+" [Warehouse] - ");stringBuilder.append(returnValue);stringBuilder.append("\n");//System.out.println(stringBuilder.toString());LogHelper.getInstatnce("inOutPutLog").write(stringBuilder.toString());}}
    /**     * ServiceLayer     * 方法执行的前调用        * @param joinPoint     */@Before("execution(* com.thc.warehouse.service.impl.*.*(..))")    public void doBeforeInServiceLayer(JoinPoint joinPoint) {  if(logSwitch){SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(df.format(new Date())+" INFO "+joinPoint.getTarget().getClass().getName()+":"+joinPoint.getSignature().getName()+" [Warehouse] - ");for (int i = 0; i < joinPoint.getArgs().length; i++) {JSONObject data = new JSONObject();data.put("requestParameter", joinPoint.getArgs()[i]);stringBuilder.append(data); }stringBuilder.append("\n");//System.out.println(stringBuilder.toString());LogHelper.getInstatnce("inOutPutLog").write(stringBuilder.toString());}    }/** * ServiceLayer * 方法执行的后返回值    * @param joinPoint * @param returnValue */@AfterReturning(pointcut = "execution (* com.thc.warehouse.service.impl.*.*(..))", returning = "returnValue")public void doAfterReturnInServiceLayer(JoinPoint joinPoint, Object returnValue) {if(logSwitch){SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(df.format(new Date())+" INFO "+joinPoint.getTarget().getClass().getName()+":"+joinPoint.getSignature().getName()+" [Warehouse] - ");JSONObject data = new JSONObject();data.put("returnValue", returnValue);stringBuilder.append(data); stringBuilder.append("\n");//System.out.println(stringBuilder.toString());LogHelper.getInstatnce("inOutPutLog").write(stringBuilder.toString());}}/** * ServiceLayer * 方法执行的后调用   * @param joinPoint */@After("execution(* com.thc.warehouse.service.impl.*.*(..))")    public void doAfterInServiceLayer(JoinPoint joinPoint) {  if(logSwitch){SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");//System.out.println(df.format(new Date())+" @After:doAfterInServiceLayer ");StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(df.format(new Date())+" @After:doAfterInServiceLayer ");LogHelper.getInstatnce("inOutPutLog").write(stringBuilder.toString());}    }/** * Debug日志 * @param className 类名称 * @param funName 方法名称 * @param content debug内容 */    public void logRecordForDebug(String className, String funName, String content) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(df.format(new Date())+" DEBUG "+className+":"+funName+" [Warehouse] - ");stringBuilder.append(content);  System.out.println(stringBuilder.toString());LogHelper.getInstatnce("inOutPutLog").write(stringBuilder.toString());    }

}

第二种配置方法:基于配置文件的配置

  1. 创建一个Java文件,并指定一个用于执行拦截的方法(LogHandler)
  2. 在SpringMVC配置文件中注册该Java类为一个Bean
  3. 在SpringMVC使用<aop:config/>、<aop:aspect/>等标签进行配置
1、建一个文件进行相关的代理操作

package com.thc.warehouse.controller.util;import java.text.SimpleDateFormat;import java.util.Date;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import com.thc.warehouse.controller.api.log.LogHelper;public class LogHandler {static{System.out.println("-----------已加载LogHandler-----------");//查看LogHandler是否加载}    //方法执行的前调用     public void doBeforeInServiceLayer(JoinPoint joinPoint) {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(df.format(new Date())+"====================================@Before:doBeforeInServiceLayer====================================");        StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@Before:doBeforeInServiceLayer====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }    //方法执行的后调用     public void doAfterInServiceLayer(JoinPoint joinPoint) {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(df.format(new Date())+"====================================@After:doAfterInServiceLayer====================================");        StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@After:doAfterInServiceLayer====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }    //方法执行的前后调用     public void doAround(ProceedingJoinPoint pjp) throws Throwable {      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");StringBuilder sb = new StringBuilder();sb.append(df.format(new Date())+"====================================@Around:doAround====================================");System.out.println(df.format(new Date())+"====================================@Around:doAround====================================");LogHelper.getInstatnce("inOutPutLog").write(sb.toString());    }        //方法运行出现异常时调用      public void doAfterThrowing(Exception ex){      System.out.println("====================================@AfterThrowing:doAfterThrowing====================================");        System.out.println(ex);      }}
2、在SpringMVC配置文件中注册该Java类为一个Bean,使用<aop:config/>、<aop:aspect/>等标签进行配置

<!--配置文件实现AOP切面 --><bean id="logHandler" class="com.thc.warehouse.controller.util.LogHandler" /><aop:config>            <aop:aspect id="addInOutPutLog" ref="logHandler">                <aop:pointcut id="addAllMethod" expression="execution(* com.thc.warehouse.controller.*.*.*(..))" />                <aop:before method="doBeforeInServiceLayer" pointcut-ref="addAllMethod" />                <aop:after method="doAfterInServiceLayer" pointcut-ref="addAllMethod" />            </aop:aspect>        </aop:config>

3、如果切Service层,需要将Spring.xml中的Service初始化bean放到SpringMVC.xml中,AOP的配置也在SpringMVC中
<context:component-scan base-package="com.thc.warehouse.dao" />