最近体系被扫出来还在运用老旧的log4j,需求晋级到最新的log4j。但是在晋级的发现,Java相关的日志处理库有log4j, log4j2,slf4j和logback,初一看的确有点头大,那么差异是啥呢?之前也大约知道一些,查找了很多相关材料,这儿好好总结一下,相信你读完就会熟练掌握。

Log4J、Log4J2和LogBack的前史故事

运用过Log4J和LogBack的同学肯定能发现,这两个结构的规划理念极为相似,运用方法也千篇一律。其实这个两个结构的作者都是一个人,Ceki Glc,俄罗斯程序员

Log4J 最初是根据Java开发的日志结构,发展一段时刻后,作者Ceki Glc将Log4j捐献给了Apache软件基金会,使之成为了Apache日志服务的一个子项目。 又因为Log4J超卓的表现,后续又被孵化出了支持C, C++, C#, Perl, Python, Ruby等语言的子结构。

然而,巨大的程序员如同都比较有特性。Ceki Glc因为不满Apache对Log4J的管理,决议不再参与Log4J的开发保护。“出走”后的Ceki Glc重整旗鼓,开发出了LogBack这个结构(SLF4J是和LogBack一起开发出来的)。LogBack改善了很多Log4J的缺陷,在功能上有了很大的提高,同时运用方法简直和Log4J相同,许多用户开端渐渐开端运用LogBack。

因为遭到LogBack的冲击,Log4J开端式微。终于,2015年9月,Apache软件基金业宣告,Log4j不在保护,建议一切相关项目晋级到Log4j2。Log4J2是Apache开发的一个新的日志结构,改善了很多Log4J的缺陷,同时也借鉴了LogBack,号称在功能上也是完胜LogBack。功能这块后边我会细心分析。

那slf4j和这些有什么联系?

SLF4J的全称是Simple Logging Facade for Java,slf4j是门面形式的典型应用,因此在讲slf4j前,需求简略介绍下门面形式。

看看门面形式再说

下面是门面形式的一个典型调用进程,其中心为外部与一个子体系的通讯有必要经过一个一致的外观目标进行,使得子体系更易于运用。 下图中客户端不需求直接调用几个子体系,只需求与一致的门面进行通讯即可。

带你深入Java Log框架,彻底搞懂Log4J、Log4J2、LogBack,SLF4J

门面形式的中心为Facade即门面目标,中心为几个点:

  • 知道一切子角色的功能和责任。

  • 将客户端发来的请求委派到子体系中,没有实际事务逻辑。

  • 不参与子体系内事务逻辑的完成。

为什么要运用slf4j ?

答复这个问题之前,咱们先看看假如需求用上面几个日志结构来打印日志,一般怎样做,详细代码如下:


// 运用log4j,需求log4j.jar
import org.apache.log4j.Logger;
Logger logger_log4j = Logger.getLogger(Test.class);
logger_log4j.info("Hello World!");
// 运用log4j2,需求log4j-api.jar、log4j-core.jar
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Logger logger_log4j2 = LogManager.getLogger(Test.class);
logger_log4j2.info("Hello World!");
// logback,需求logback-classic.jar、logback-core.jar
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
Logger logger_logback = new LoggerContext().getLogger(Test.class);
logger_logback.info("Hello World!");

从上面不难看出,运用不同的日志结构,就要引进不同的jar包,运用不同的代码获取Logger。假如项目晋级需求更换不同的结构,那么就需求修正一切的当地来获取新的Logger,这将会产生巨大的工作量。

根据此,咱们需求一种接口来将不同的日志结构的运用一致起来,这也是为什么要运用slf4j的原因。

SLF4J,即简略日志门面(Simple Logging Facade for Java),不是详细的日志解决方案,它只服务于各式各样的日志体系。依照官方的说法,SLF4J是一个用于日志体系的简略Facade,答应最终用户在布置其应用时运用其所希望的日志体系。

留意:相似的日志门面还有Jakarta Common logging(JCL),首要差异在于,SLF4J是一个比较新的日志结构,它愈加灵活,功能更好,支持更多的日志完成,并且JCL根据classLoader在运转时动态加载日志结构,可能会产生很多意想不到的安全问题,关于这个问题能够参阅讨论stackoverflow.com/questions/3…

经过上面的介绍,咱们能够知道JCL和SLF4J都是日志门面(Facade),而Log4J、Log4J2和LogBack都是子体系角色(SunSystem),也便是详细的日志完成结构。他们的联系如下,JUL是JDK自身供给的一种完成。

带你深入Java Log框架,彻底搞懂Log4J、Log4J2、LogBack,SLF4J

运用日志门面引进日志组件的最大优势是:将体系和详细的日志完成结构解耦合。

slf4j怎样和日志结构结合运用?

运用slf4j后,当咱们在打印日志时,就能够运用下面的方法:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(Test.class);
logger.info("Hello World!")

这又引进了另外一个问题,slf4j怎样决议运用哪个结构日志呢,并且引进哪些jar包呢?官方为咱们预备了下面的组合依靠结构图:

带你深入Java Log框架,彻底搞懂Log4J、Log4J2、LogBack,SLF4J

总结来说,便是一下几种:

  • slf4j + logback: slf4j-api.jar + logback-classic.jar + logback-core.jar

  • slf4j + log4j: slf4j-api.jar + slf4j-log412.jar + log4j.jar

  • slf4j + jul: slf4j-api.jar + slf4j-jdk14.jar

  • 也能够只用slf4j无日志完成:slf4j-api.jar + slf4j-nop.jar

留意到这儿没有log4j2依靠jar的联系,和log4j2配合需求导入log4j2的log4j-api.jar、log4j-core.jar和桥接包log4j-slf4j-impl.jar。

所谓的桥接包,便是完成StaticLoggerBinder类,用来连接slf4j和日志结构。因为log4j和log4j2刚开端没有StaticLoggerBinder这个类,为了不改变程序结构,只能重新写一个新的jar来完成StaticLoggerBinder。而logback出现slf4j之后,所以在logback自身的jar中完成了StaticLoggerBinder,所以就不需求桥接包。

现在为什么推荐Log4j2?

Apache Log4j 2 是 Log4j(1) 的晋级版,比它的先人 Log4j 1.x和logback 有了很大的改善。除了内部规划的调整外,首要有以下几点的大晋级:

  • 更简化的装备

  • 更强大的参数格局化

  • 最夸张的异步功能

Log4j2 中,分为 API(log4j-api)和完成 (log4j-core) 两个模块。API 和 slf4j 是一个类型,归于日志抽象 / 门面,而完成部分,才是 Log4j 2 的中心。

  • org.apache.logging.log4j log4j-api

  • org.apache.logging.log4j log4j-core

log4j2 在目前 JAVA 中的日志结构里,异步日志的功能是最高的,详细能够看下面的对比图:

带你深入Java Log框架,彻底搞懂Log4J、Log4J2、LogBack,SLF4J

log4j2采用了字符串复用等手段来完成零GC形式运转,另外供给了MemoryMappedFileAppender完成了极高的IO功能,并且API比较slf4j,有更丰富的参数格局化功能。

怎样运用Log4j2?

说了半天,那怎样去运用Log4j2呢,这部分我细心介绍一下。

常规运用过程

下面以Maven装备为例进行介绍。

  1. 首先需求参加log4j的依靠,留意2.17.1以下的版别会有远程代码执行的安全漏洞,详细参阅Apache官方文档(logging.apache.org/log4j/2.x/s…

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
  1. 装备好log4j2.xml装备文件,并放到项意图classpath里边,一般会放到resources目录里边。下面是一个参阅的装备,当然也能够运用properties文件和yaml文件来进行装备。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.malaoshi" level="debug" additivity="false">
<AppenderRef ref="LogToConsole"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>
  1. 在程序中打印log。运用下面的方法来打印log即可。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HelloWorld {
private static final Logger logger = LogManager.getLogger(HelloWorld.class);
public static void main(String[] args) {
logger.debug("Hello from Log4j 2");
// in old days, we need to check the log level to increase performance
/*if (logger.isDebugEnabled()) {
logger.debug("{}", getNumber());
}*/
// with Java 8, we can do this, no need to check the log level
logger.debug("{}", () -> getNumber());
}
static int getNumber() {
return 5;
}
}

输出如下:


19:12:25.337 [main] DEBUG com.malaoshi.HelloWorld - Hello from Log4j 2
19:12:25.340 [main] DEBUG com.malaoshi.HelloWorld - 5

装备文件详解

首先给咱们看一个比较全面的装备文件,可能看起来比较头大,不急我后边会一点点细心分析。


<?xml version="1.0" encoding="UTF-8"?>
<!--日志等级以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后边的status,这个用于设置log4j2自身内部的信息输出,能够不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够主动检测修正装备 文件和重新装备自身,设置距离秒数-->
<configuration status="WARN" monitorInterval="30">
<!--先界说一切的appender-->
<appenders>
<!--这个输出控制台的装备-->
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格局-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--文件会打印出一切信息,这个log每次运转程序会主动清空,由append特点决议,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出一切的info及以下等级的信息,每次巨细超过size,则这size巨细的日志会主动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上等级的信息(onMatch),其他的直接回绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy特点如不设置,则默以为最多同一文件夹下7个文件,这儿设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--然后界说logger,只要界说了logger并引进的appender,appender才会收效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>

下面为咱们细心解析这个装备文件,

1. 根节点

Configuration有两个特点:status和monitorinterval,有两个子节点:Appenders和Loggers(标明能够界说多个Appender和Logger)。

  • status用来指定log4j自身的打印日志的等级.

  • monitorinterval用于指定log4j主动重新装备的监测距离时刻,单位是s,最小是5s.

2. Appenders节点

Appenders节点,常见的子节点有:Console、RollingFile、File。

Console节点用来界说输出到控制台的Appender。

  • name:指定Appender的姓名。

  • target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT。

  • PatternLayout:输出格局,不设置默以为:%m%n。

File节点用来界说输出到指定位置的文件的Appender。

  • name:指定Appender的姓名。

  • fileName:指定输出日志的意图文件带全途径的文件名。

  • PatternLayout:输出格局,不设置默以为:%m%n。

RollingFile节点用来界说超过指定巨细主动删去旧的创立新的的Appender。

  • name:指定Appender的姓名。

  • fileName:指定输出日志的意图文件带全途径的文件名。

  • PatternLayout:输出格局,不设置默以为:%m%n。

  • filePattern:指定新建日志文件的名称格局。

  • Policies是指定翻滚日志的战略,便是什么时候进行新建日志文件输出日志。

TimeBasedTriggeringPolicy:Policies子节点,根据时刻的翻滚战略,interval特点用来指定多久翻滚一次,默认是1 hour。modulate=true用来调整时刻:比方现在是早上3am,interval是4,那么第一次翻滚是在4am,接着是8am,12am…而不是7am。

SizeBasedTriggeringPolicy:Policies子节点,根据指定文件巨细的翻滚战略,size特点用来界说每个日志文件的巨细。

  • DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开端删去最旧的,创立新的(经过max特点)。

3. Loggers

Loggers节点,常见子节点有:Root和Logger。

Root节点用来指定项意图根日志,假如没有单独指定Logger,那么就会默认运用该Root日志输出

  • level:日志输出等级,共有8个等级,依照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。

  • AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender。

Logger节点用来单独指定日志的形式,比方要为指定包下的class指定不同的日志等级等。

  • level:日志输出等级,共有8个等级,依照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。

  • name:用来指定该Logger所适用的类或许类所在的包全途径,承继自Root节点。

  • AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,假如没有指定,就会默认承继自Root.假如指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此刻咱们能够设置Logger的additivity=”false”只在自界说的Appender中进行输出。

4. 日志Level

共有8个等级,依照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。

  • All:最低等级的,用于打开一切日志记录。

  • Trace:是追踪,便是程序推进以下,你就能够写个trace输出,所以trace应该会特别多,不过没联系,咱们能够设置最低日志等级不让他输出。

  • Debug:指出细粒度信息事件对调试应用程序是非常有协助的。

  • Info:消息在粗粒度等级上突出强调应用程序的运转进程。

  • Warn:输出正告及warn以下等级的日志。

  • Error:输出错误信息日志。

  • Fatal:输出每个严峻的错误事件将会导致应用程序的退出的日志。

  • OFF:最高等级的,用于关闭一切日志记录。

程序会打印高于或等于所设置等级的日志,设置的日志等级越高,打印出来的日志就越少。

参阅范例

这部分给咱们供给几个常用的装备,直接放到项目里边改一下包名就能够用。

1. 输出日志到Console


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<!-- avoid duplicated logs with additivity=false -->
<Logger name="com.mkyong" level="debug" additivity="false">
<AppenderRef ref="LogToConsole"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>

2. 输出日志到文件


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<File name="LogToFile" fileName="logs/app.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
</Appenders>
<Loggers>
<Logger name="com.malaoshi" level="debug" additivity="false">
<AppenderRef ref="LogToFile"/>
<AppenderRef ref="LogToConsole"/>
</Logger>
<Logger name="org.springframework.boot" level="error" additivity="false">
<AppenderRef ref="LogToConsole"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToFile"/>
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>

3. 输出到文件并翻滚生成新的日志文件


<Configuration status="DEBUG">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="LogToRollingFile" fileName="logs/app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!-- avoid duplicated logs with additivity=false -->
<Logger name="com.malaoshi" level="debug" additivity="false">
<AppenderRef ref="LogToRollingFile"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>

以上便是一切的内容,看完下来就会搞清楚各种log结构的联系,以及怎样在项目中运用这些log结构。


参阅:

  • mkyong.com/logging/apa…

  • www.zybuluo.com/wxf/note/13…

  • www.cnblogs.com/xrq730/p/86…

  • www.cnblogs.com/54chensongx…

  • segmentfault.com/a/119000003…

  • /post/703302…

能够重视公众号【码老思】,第一时刻获取最通俗易懂的原创技术干货。