Log4j2简介:
Apache Log4j2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
- 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制
-
性能提升,log4j2相较于log4j和logback都具有很明显的性能提升,后面会有官方测试的数据。
- 自动重载配置,参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
-
无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm的gc
官网 https://logging.apache.org/log4j/2.x/
Log4j2日志级别:
- trace:追踪,是最低的日志级别,相当于追踪程序的执行,一般不怎么使用
- debug:一般在开发中,都将其设置为最低的日志级别.
- info:输出感兴趣的信息,我在开发中一般都会用info去输出.
- warn:警告.有些时候,虽然程序不会报错,但是还是需要告诉程序员的.
- error:错误,这个在开发中也挺常用的,,
- fetal:极其重大的错误,这个一旦发生,程序基本上也要停止了,,目前限于开发经验,我还没有碰到要碰到它的地方.
Springboot整合:
步骤一: 添加场景依赖:
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <version> 2.2.4.RELEASE</version> </dependency>
步骤二: 注释掉springboot的自带log框架
说明:由于springboot也有自带log框架 会与log4j2产生冲突 会导致其不可用
1.注释掉springboot父类自带依赖
ctrl+f 搜索 <artifactId>spring-boot-starter-logging</artifactId> 将其注释掉
2. 注释掉已经构建maven的logging依赖
xml配置文件及使用说明
1. 配置xml
说明:
- log4j2是用xml去定义需要的配置的,其中涉及到两个重要的标签,appenders和loggers.appenders顾名思义,代表将其信息输出到哪里,在这个标签内部定义了输出位置的相关信息 .
- appenders标签内部可以有多个appender,表示可以输出到多个位置.这些appender用具体的标签去标识,例如<console>表示输出到控制台的appender,<RollingFile>表示输出到文件中.appender内部可以定义输出的格式(用<pattern>标签去标识.),可以定义在达到某个日志级别的时候才给予输出,其余情况拦截(用<ThreadSholdFilter>标签去标识.).
- loggers标签则是定义了一些必要的logger,logger代表用于输出日志信息的具体对象.logger内部有appender,指定这些对象输出的具体位置.可以定义多个appender,其中,logger的定义比较特殊,因为它涉及到了继承,可以结合log4j2的配置文件来看一看.注意log4j2.xml放在classpath下,不需要任何配置就可以加载.
详细配置样例
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--<Configuration status="WARN" monitorInterval="30"> -->
<properties>
<property name="LOG_HOME">D:logs</property>
</properties>
<Appenders>
<!--*********************控制台日志***********************-->
<Console name="consoleAppender" target="SYSTEM_OUT">
<!--设置日志格式及颜色-->
<PatternLayout
pattern="[%style{%d{HH:mm:ss:SSS}}{bright,green}] [%highlight{%-5level}] [%style{%t}{bright,blue}] %style{%l}{bright,yellow}: %msg%n%style{%throwable}{red}"
disableAnsi="false" noConsoleNoAnsi="false"/>
</Console>
<!--*********************文件日志***********************-->
<!--all级别日志-->
<RollingFile name="allFileAppender"
fileName="${LOG_HOME}/all.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log">
<!--设置日志格式-->
<PatternLayout>
<pattern>%d %p %l [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 设置日志文件切分参数 -->
<!--<OnStartupTriggeringPolicy/>-->
<!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
<SizeBasedTriggeringPolicy size="100 MB"/>
<!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
<DefaultRolloverStrategy max="100"/>
</RollingFile>
<!--debug级别日志-->
<RollingFile name="debugFileAppender"
fileName="${LOG_HOME}/debug.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log">
<Filters>
<!--过滤掉info及更高级别日志-->
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!--设置日志格式-->
<PatternLayout>
<pattern>%d %p %l [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 设置日志文件切分参数 -->
<!--<OnStartupTriggeringPolicy/>-->
<!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
<SizeBasedTriggeringPolicy size="100 MB"/>
<!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
<DefaultRolloverStrategy max="100"/>
</RollingFile>
<!--info级别日志-->
<RollingFile name="infoFileAppender"
fileName="${LOG_HOME}/info.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<Filters>
<!--过滤掉warn及更高级别日志-->
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!--设置日志格式-->
<PatternLayout>
<pattern>%d %p %l [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 设置日志文件切分参数 -->
<!--<OnStartupTriggeringPolicy/>-->
<!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
<SizeBasedTriggeringPolicy size="100 MB"/>
<!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
<DefaultRolloverStrategy max="100"/>
</Policies>
</RollingFile>
<!--warn级别日志-->
<RollingFile name="warnFileAppender"
fileName="${LOG_HOME}/warn.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<Filters>
<!--过滤掉error及更高级别日志-->
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!--设置日志格式-->
<PatternLayout>
<pattern>%d %p %l [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 设置日志文件切分参数 -->
<!--<OnStartupTriggeringPolicy/>-->
<!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
<SizeBasedTriggeringPolicy size="100 MB"/>
<!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
<DefaultRolloverStrategy max="100"/>
</RollingFile>
<!--error及更高级别日志-->
<RollingFile name="errorFileAppender"
fileName="${LOG_HOME}/error.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<!--设置日志格式-->
<PatternLayout>
<pattern>%d %p %l [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 设置日志文件切分参数 -->
<!--<OnStartupTriggeringPolicy/>-->
<!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
<SizeBasedTriggeringPolicy size="100 MB"/>
<!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
<DefaultRolloverStrategy max="100"/>
</RollingFile>
<!--json格式error级别日志-->
<RollingFile name="errorJsonAppender"
fileName="${LOG_HOME}/error-json.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/error-json-%d{yyyy-MM-dd}-%i.log">
<JSONLayout compact="true" eventEol="true" locationInfo="true"/>
<Policies>
<SizeBasedTriggeringPolicy size="100 MB"/>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!-- 根日志设置-->
<Root level="debug">
<AppenderRef ref="allFileAppender" level="all"/>
<AppenderRef ref="consoleAppender" level="debug"/>
<AppenderRef ref="debugFileAppender" level="debug"/>
<AppenderRef ref="infoFileAppender" level="info"/>
<AppenderRef ref="warnFileAppender" level="warn"/>
<AppenderRef ref="errorFileAppender" level="error"/>
<AppenderRef ref="errorJsonAppender" level="error"/>
</Root>
<!--spring日志-->
<Logger name="org.springframework" level="error"/>
<Logger name="io.netty" level="error"/>
<Logger name="io.lettuce" level="error"/>
<!--druid数据源日志-->
<Logger name="druid.sql.Statement" level="warn"/>
<!-- mybatis日志 -->
<Logger name="com.mybatis" level="warn"/>
<Logger name="org.hibernate" level="warn"/>
<Logger name="com.zaxxer.hikari" level="info"/>
<Logger name="org.quartz" level="info"/>
<Logger name="com.andya.demo" level="debug"/>
</Loggers>
</Configuration>
说明: <properties>标签定义了在配置文件上下文可能会用到的键值对的信息(类似于maven).重点看看<loggers>标签,首先是root标签.在log4j2默认所有的日志输出对象(Loggger)都支持<root>标签的相关配置.描述起来可能有些晦涩,但是只要看看Logger对象是如何在类中获取就比较简单了.下面代码是在类中获取Logger对象的方式:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* Author: LSP
**/
@Component
public class Run implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
Logger root = LogManager.getLogger(Run.class);
root.info("啊啊啊啊啊啊啊啊啊啊啊");
}
}
这个参数是传入Class对象,并且返回一个Logger.实际上还可以传入一个字符串,如下:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * Author: LSP **/ @Component public class Run implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { Logger root1 = LogManager.getLogger("aaa"); root1.info("呃呃呃呃呃呃鹅鹅鹅呃呃呃"); } }
说明:
- 但是实际上我们传入的参数是各种各样的,log4j2怎么知道我们需要返回什么样的Logger呢?实际上所有的Logger默认都继承一个配置,就是<root>.log4j2根据name去配置文件依次与各个logger的name互相匹配,寻找对应的logger配置信息.当我们传入的名称为字符串时 会直接匹配Logger标签的name 如果找到了,则采用该<logger>的配置信息,如果没有找到,则采用默认的配置信息,也就是<root>中定义的配置信息,可以看出,我们在<root>中可以将日志信息输出到多个位置 且会有适配等级. 当我们传入的参数为Class对象的时候,实际上name为该Class对象的全类名.
- 此外,在配置文件中定义name也是有"讲究"的,前面提到,在Logger对象没有在xml中找到匹配的name的时候,会采用<root>中定义的配置信息,实际上在我们定义<Logger>标签的配置的时候,它默认也是继承<root>中的配置信息的,也就是说,某个具体的<Logger>标签在默认情况下会继承<root>的所有配置.可以设置additivity为false,就可以不使用父类的配置信息.
- 此外,Logger标签相互之间也是有继承的关系的.也就是一个Logger标签的配置信息可以继承另外一个<Logger>标签内部的配置信息.这个log4j2通过name去实现.当一个logger的name是另外一个logger的name的前缀的时候,则称该Logger是另外一个Logger的"父".例如:"com.hlhdidi.web"为name的<Logger>的配置可以继承"com.hlhdidi"为name的<logger>的配置信息.同样也可以使用additivity属性指定不继承.
- 此外,也可以在<Logger>标签上指定level属性.它表示指定该Logger输出日志的最低日志级别.但是有一种特殊情况需要注意:考虑下面的情况:定义了一个<Logger>它的日志级别为info,设置输出到console,<root>的日志级别也定义为info,设置输出到console.当我们使用.info输出信息的时候,如果匹配到此<Logger>,那么根据继承的关系,此时控制台上同一个信息会被输出两遍.这时候,如果我们将<root>的日志级别改为error,继续使用.info输出信息,(使用的Logger依然能匹配到配置文件上的一个具体的<Logger>)那么信息将会输出几次呢?通过输出发现还是两次.我推测可能是log4j内部只要匹配到一个<Logger>,在继承的时候,将不会再次去判断是否符合父类的日志级别.
格式标签说明:
%d 日期
%p 日志等级
%l 函数行数
%t 线程名称
%m%n 输入信息