Spring Boot使用AOP搭建统一处理请求日志和使用log4j记录不同级别的日志

http://blog.didispace.com/springbootaoplog/启发,今天给Spring Boot项目搭建了统一处理请求日志的切面并引入log4j记录不同层级日志。 mark一下这个过程,以及原文中没有涉及到的一些疑问

一.  新增要使用的依赖 

<!--日志-->
<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>
<!--aop-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
   <version>2.0.4.RELEASE</version>
</dependency>

在新导入依赖的过程中刚好遇到公司网络从maven中央仓库下载依赖受阻,所以也切换了项目的依赖源,在此顺便记录一下

   修改apache-maven-3.3.9中的conf文件夹下的setting.xml文件内容,在<mirrors>节点下新增

<mirror>
  <id>alimaven</id> 
  <name>aliyun maven</name> 
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url> 
  <mirrorOf>central</mirrorOf> 
</mirror>

再将IDEA中maven配置的“User settings file”修改成setting.xml文件所在路径(默认路径/Users/xxx/.m2/setting.xml)

二.实现Web层的日志切面,关于这个类的相关疑问可以参考顶部链接的文章

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Aspect
@Order(0)
@Component
public class WebLogAspect {

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    private Logger logger = Logger.getLogger(getClass());

    @Pointcut("execution(public * com.example.controller..*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        startTime.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();

            // 记录下请求内容
            System.out.println("\r\n");
            logger.info("地址 : " + request.getRequestURL().toString());
            logger.info("请求方式 : " + request.getMethod());
            logger.info("IP : " + request.getRemoteAddr());
            logger.info("执行的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            Object[] args = joinPoint.getArgs().clone();
            logger.info("参数 : " + Arrays.toString(args));
        }
    }

    /**
     * 处理完请求,返回内容
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) {
        logger.info("返回内容 : " + ret);
        logger.info("花费时间 : " + (System.currentTimeMillis() - startTime.get()) + "毫秒");
    }
}

通过@Pointcut定义切入点,此处是com.example.controller包下的所有Controller(对controller层所有请求处理做切入点),然后通过@Before实现对请求内容的日志记录,最后通过@AfterReturning记录请求返回的对象。

实现AOP的切面主要有以下几个要素:(转)

  • 使用@Aspect注解将一个java类定义为切面类
  • 使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
  • 根据需要在切入点不同位置的切入内容
    • 使用@Before在切入点开始处切入内容
    • 使用@After在切入点结尾处切入内容
    • 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
    • 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容
    • 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑

三. 使用log4j.properties记录更详细的日志

在resources文件夹下与application.yml文件同级目录 创建log4j.properties,这样将在项目根目录下创建一个logs的目录,用于存放不同的日志文件。

andlers= java.util.logging.ConsoleHandler
redirect.commons.logging = enabled

log4j.rootLogger=DEBUG, STDOUT, FILE, DAILY_FILE, ROLLING_FILE

### 输出DEBUG 级别以上的日志 ###
log4j.appender.DEBUG = org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUG.File = logs/debug.log
log4j.appender.DEBUG.Append = true
log4j.appender.DEBUG.Threshold = DEBUG 
log4j.appender.DEBUG.layout = org.apache.log4j.PatternLayout
log4j.appender.DEBUG.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### direct log messages to stdout ###
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.Target=System.out
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - %c{1}:%L -%-4r - %-5p - %m%n

### direct log messages to file ###
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=logs/file.log
###log4j.appender.FILE.File=${webapp.root}/WEB-INF/logs/file.log ###
### log4j:ERROR setFile(null,true) call failed.java.io.FileNotFoundException ###
log4j.appender.FILE.Append=true
log4j.appender.FILE.Threshold=ERROR
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### direct log messages to daily file ###
log4j.appender.DAILY_FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DAILY_FILE.File=logs/daily.log
log4j.appender.DAILY_FILE.Encoding=UTF-8
log4j.appender.DAILY_FILE.Threshold=INFO
log4j.appender.DAILY_FILE.DatePattern='.'yyyy-MM-dd
log4j.appender.DAILY_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.DAILY_FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L : %m%n

### direct log messages to rolling file ###
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=logs/rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.CONSOLE_FILE.Encoding=UTF-8
log4j.appender.ROLLING_FILE.MaxFileSize=1000KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %-d{yyyy-MM-dd HH:mm:ss} - %c -%-4r [%t] %-5p %c %x - %m%n

### direct log messages to socket ###
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000

四. 搭建过程中遇到的一些疑问

  1. 报错:log4j:WARN No appenders could be found for logger (com.example.aspect.WebLogAspect).
                 log4j:WARN Please initialize the log4j system properly.
                 log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 

由于log4j.properties文件配置不够完整,使用上述的文件就可以了

 2. Log4J配置后,启动项目时控制台出现  log4j:ERROR setFile(null,true) call failed. 报错:java.io.FileNotFoundException

由于之前log4j.properties文件中配置的是log4j.appender.FILE.File=${webapp.root}/WEB-INF/logs/file.log。tomcat中也有这个同名的文件,tomcat启动是默认去找log4j.properties,但此时Listener还没有起来,tomcat就要往/WEB-INF/logs/log4j.log 写日志就找不到了。 解决: 将路径改成 logs/xxx.log, 这样就将这些不同日志文件生成在项目根目录下的logs目录中。

3. 关于Spring Boot项目中使用AOP是否需要在yml文件中配置

根据查阅各位大佬的文章发现,Spring BootAOP的默认配置属性是开启的,也就是说spring.aop.auto属性的值默认是true;同时我们只要引入了AOP依赖后,默认就已经增加了@EnableAspectJAutoProxy功能,不需要我们在程序启动类上面加入注解@EnableAspectJAutoProxy,也不需要在yml中配置spring.aop.proxy-target-class,不过这个默认值是false即使用的是JDK动态代理,当我们需要使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true。

猜你喜欢

转载自blog.csdn.net/sinat_29108239/article/details/83583705