受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
Boot
对AOP
的默认配置属性是开启的,也就是说spring.aop.auto
属性的值默认是true;
同时我们只要引入了AOP
依赖后,默认就已经增加了@EnableAspectJAutoProxy
功能,不需要我们在程序启动类上面加入注解@EnableAspectJAutoProxy,也不需要在yml中配置spring.aop.proxy-target-class,不过这个默认值是false即使用的是JDK动态代理,
当我们需要使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true。