Spring Aop整合log4j2 分类打印日志
0. 概述
在Java生态体系中,围绕着日志,有很多成熟的解决方案。关于日志输出,主要有两类工具。
一类是日志框架,主要用来进行日志的输出的,比如输出到哪个文件,日志格式如何等。 LogBack Log4j Log4j2 java.util.logging
另外一类是日志门面,主要一套通用的API,用来屏蔽各个日志框架之间的差异的。SLF4J commons-logging
所以,对于Java工程师来说,关于日志工具的使用,最佳实践就是在应用中使用如Log4j + SLF4J 这样的组合来进行日志输出
1. log4j配置详解
参考地址:Springboot整合log4j2日志全解 https://www.cnblogs.com/keeya/p/10101547.html
log4j2.xml配置想达到的效果:error输出到error的文件,info输出到info的文件。主要思想就是root的级别最低为all,然后分别在各个级别文件RollingFile中去配那些要那些不要顺序不能错,先配更高级别的,
首先要过滤更高级别的,将其NEUTRAL交给下一个高级别的过滤器处理, 不符合的日志级别,把不需要的DENY掉,然后再ACCEPT需要的日志级别,如下面的Filters
<Filters>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
以下详细讲解log4j的配置文件 <?xml version="1.0" encoding="UTF-8"?> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration monitorInterval="5"> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--变量配置--> <Properties> <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--> <!-- %logger{36} 表示 Logger 名字最长36个字符 --> <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" /> <!-- 定义日志存储的路径 --> <property name="FILE_PATH" value="更换为你的日志路径" /> <property name="FILE_NAME" value="更换为你的项目名" /> <!--这一堆property就相当于配了一堆变量,以怎样的格式输出到哪里,,等等,下面引用方便 --> </Properties> <appenders> <console name="Console" target="SYSTEM_OUT"> <!--输出日志的格式--> <PatternLayout pattern="${LOG_PATTERN}"/> <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> </console> <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false"> <PatternLayout pattern="${LOG_PATTERN}"/> </File> <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> </appenders> <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--> <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--> <loggers> <logger name="sysInfo" level="info" additivity="false"> <appender-ref ref="InfoFile"/> </logger> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.mybatis" level="info" additivity="false"> <AppenderRef ref="Console"/> </logger> <!--监控系统信息--> <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger(root) 的appender里输出。--> <Logger name="org.springframework" level="info" additivity="false"> <AppenderRef ref="Console"/> </Logger> <!--root这里配的appender意思是系统里面所有的info级别的日志都会打印到下面配的appender--> <!--如何实现分类打印日志呢:主要思想就是root的级别最低为all,然后分别在各个级别文件RollingFile中去配那些要那些不要 顺序不能错,先配更高级别的,首先要过滤更高级别的,将其NEUTRAL交给下一个高级别的过滤器处理, 不符合的日志级别,把不需要的DENY掉,然后再ACCEPT需要的日志级别--> <root level="info"> <appender-ref ref="Console"/> <!--<appender-ref ref="Filelog"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/>--> </root> </loggers> </configuration>
2. Spring Aop 打印日志
import java.util.ArrayList; import java.util.Arrays; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; 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.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSONObject; //pom.xml添加依赖的时候注意排除springBoot自动加载的logging @Aspect @Configuration public class AspectController { // private final static Logger logger = LoggerFactory.getLogger(AspectController.class); //这种方法需要把log4j配置文件的root配成也打在文件,而不只是打在控制台。这种打出来的效果就是会把所有的info信息打印,有些不是我们想要的,推荐用下面的方法 Log infoLog = LogFactory.getLog("sysInfo"); //sysInfo是log4j里面最后配的logger,获取指定的logger,自己下面想打什么就只打什么 @Pointcut("execution(* XXX.controller.*.*(..))") public void printMsg(){ // implementation ignored } @Around("printMsg()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); String url = request.getRequestURL().toString(); String method = request.getMethod(); String uri = request.getRequestURI(); // String queryString = request.getQueryString(); ArrayList<Object> argList = new ArrayList<>(); // String queryString = JSONObject.toJSONString(pjp.getArgs());这种方法获取json格式的请求参数,有时会有异常,说你不是异步的请求咋的,pjp.getArgs()返回的数组中携带有Request或者Response对象,导致序列化异常。 //pjp.getArgs()是获取你的接口所有的参数,你的controller里面写了啥参数,这儿就获取到什么参数。如果controller里面需要HttpServletRequest,那这里就要过滤因为不能序列化 for (Object arg : pjp.getArgs()) { // request/response无法使用toJSON if (arg instanceof HttpServletRequest) { argList.add("request"); } else if (arg instanceof HttpServletResponse) { argList.add("response"); } else { argList.add(arg); } } // logger.info("request start, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString); infoLog.info("url: " + url + ", " + "method: " + method + ", " + "uri: " + uri); infoLog.info("params: " + argList); Object result = pjp.proceed(); // logger.info("response data: {}" , result); infoLog.info("response data: " + result); return result; } }
补充:
在logback.xml中做修改,先按照天进行分割,然后按照文件大小进行分割,文件的大小为50M,在按照总大小为10GB的时候进行删除


浙公网安备 33010602011771号