天天看点

Java常用的日志框架对比

作为Java开发人员,对于日志记录框架一定非常熟悉。而且几乎在所有应用里面,一定会用到各种各样的日志框架用来记录程序的运行信息。而对于一个成熟的Java应用,这个是必不可少的。在开发和调试阶段,日志可以帮助我们更快的定位问题;而在应用的运维过程中,日志系统又可以帮助我们记录大部分的异常信息,通常很多企业会通过收集日志信息来对系统的运行状态进行实时监控预警。

总体概览

image.png

目前的日志框架有JDK自带的

logging

log4j1

log4j2

logback

,这些框架都自己定制了日志 API ,并且有相应的实现;目前用于实现日志统一的框架

Apache commons-logging

slf4j

,遵循面向接口编程的原则,这两大框架可以让用户在程序运行期间去选择具体的日志实现系统(

log4j1\log4j2\logback

等)来记录日志,是统一抽象出来的一些接口。

日志级别

log4j

定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),

优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

ALL

最低等级的,用于打开所有日志记录。

TRACE

很低的日志级别,一般不会使用。

DEBUG

指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。

INFO

消息在粗粒度级别上突出强调应用程序的运行过程。这个可以用于生产环境中输出程序运行的一些重要信息。

WARN

表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给开发者的一些提示。

ERROR

指出发生错误的信息,可能会导致系统出错或是宕机等,必须要避免

FATAL

指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。

OFF

最高等级,用于关闭所有日志记录。

Log4j

官网地址:

https://logging.apache.org/log4j/1.2/

简介:

Apache 的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;用户也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,用户能够更加细致地控制日志的生成过程。这些可以通过一个 配置文件来灵活地进行配置,而不需要修改程序代码。

在JDK 1.3及以前,Java打日志依赖

System.out.println()

,

System.err.println()

或者

e.printStackTrace()

,Debug日志被写到

STDOUT

流,错误日志被写到

STDERR

流。这样打日志有一个非常大的缺陷,即无法定制化,且日志粒度不够细。log4j是在这样的环境下诞生的,它是一个里程碑式的框架,它定义的

Logger

Appender

Level

等概念如今已经被广泛使用。

https://mvnrepository.com/

中可以查到,log4j1从2005年11月更新到2012年3月,后面就没再更新了

最新的依赖(May 26, 2012)

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
           

2015年8月5日,项目管理委员会宣布

Log4j 1.x

已达到使用寿命。建议用户使用

Log4j 1

升级到

Apache Log4j 2

Log4j2

官网地址:

https://logging.apache.org/log4j/2.x/

Log4j2

Log4j1

的升级版本。

Log4j2

基本上把

Log4j1

版本的核心全部重构掉了,而且基于

Log4j1

做了很多优化和改变。并提供了

Logback

中可用的许多改进,同时修复了

Logback

架构中的一些固有问题。

Log4j2最新的依赖(Mar 11, 2018)

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>
           

SpringBoot也有Log4j2相关的依赖

不过

SpringBoot

自带的

jar

包已经够用了

引入依赖(Jun 14, 2018)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>
           

引入之后可以看到外部jar包多了三个关于log4j2的

关于上面的包

log4j-to-slf4j

log4j2

的接口适配到

slf4j

上,不能和

log4j-slf4j-impl

同时存在

log4j-api

包含

.class

但是只是一堆接口而已,实际使用需要

log4j

log4j-core

.class

.java

也就是源码

jul

指的是

java.util.logging

,是

java

内置的日志模块

简介:受

Log4j

启发,Sun在

Java1.4

版本中引入了

java.util.logging

,但是

jul

功能远不如

log4j

完善,开发者需要自己编写

Appenders

(Sun称之为

Handlers

),且只有两个

Handlers

可用(

Console

File

),

jul

Java1.5

以后性能和可用性才有所提升。

不是SpringBoot项目需要引用

jul

的话可以加

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jul</artifactId>
    <version>2.11.0</version>
</dependency>
           

Log4j2的使用

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SpringcloudEurekaApplication {
    static Logger logger = LogManager.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}
           

启动项目后控制台输出为

SLF4J

https://www.slf4j.org/

SLF4J(Simple Logging Facade for Java)用作各种日志框架(java.util.logging,logback,log4j)的简单外观或抽象,允许最终用户在部署 时插入所需的日志框架。

最新依赖(Mar 21, 2018)

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.8.0-beta2</version>
</dependency>
           

Springboot项目不需要引入任何依赖都可以使用

用法

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpringcloudEurekaApplication{
    private static final Logger logger = LoggerFactory.getLogger(SpringcloudEurekaApplication.class);
    public static void main(String[] args) {
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.info("Current Time: " + System.currentTimeMillis());
        logger.info("Current Time: {}", System.currentTimeMillis());
        logger.trace("trace log");
        logger.warn("warn log");
        logger.debug("debug log");
        logger.info("info log");
        logger.error("error log");
    }
}
           

通常输出日志开销非常大,

SLF4J

通过

{}

作为占位符的方式输出字符串,相比字符串拼接的方式,效率有显著的提升。

logback

https://logback.qos.ch/

logback

log4j

是同一个作者创作,它是

log4j

的升级版

Logback的体系结构足够通用,以便在不同情况下应用。

logback分为三个模块:

logback-core

logback-classic

logback-access

logback-core

模块为其他两个模块奠定了基础。

logback-classic

模块可以被同化为

log4j

的显着改进版本。

logback-classic

本身实现了

SLF4J API

,因此您可以在

logback

和其他日志框架(如

log4j

java.util.logging(JUL)

)之间来回切换。

logback-access

模块​​与

Servlet

容器(如Tomcat和Jetty)集成,以提供

HTTP

访问日志功能。可以在

logback-core

之上轻松构建自己的模块。

Logback的核心对象:Logger、Appender、Layout

Logback主要建立于

Logger

Appender

Layout

这三个类之上。

Logger

:日志的记录器,把它关联到应用的对应的

context

上后,主要用于存放日志对象,也可以定义日志类型、级别。

Logger

对象一般多定义为静态常量.

Appender

:用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、

MySQL

PostreSQL

Oracle

和其他数据库、

JMS

和远程

UNIX Syslog

守护进程等。

Layout

:负责把事件转换成字符串,格式化的日志信息的输出。

具体使用

引入依赖(Feb 11, 2018),分别对应

logback

上的三个模块

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
           

注:如果在SpringBoot下是不需要引入的

Spring Boot内部日志系统使用的是

Commons Logging

,但开放底层的日志实现。默认为会

Java Util Logging, Log4J, Log4J2和Logback

提供配置。每种情况下都会预先配置使用控制台输出,也可以使用可选的文件输出。

因为在SpringBoot中本身就内置了日志功能,在

spring-boot-starter

依赖中

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>
           

logback的配置介绍

如果没有配置文件,那么

logback

默认地会调用

BasicConfigurator

,创建一个最小化配置。最小化配置由一个关联到根

logger

ConsoleAppender

组成。输出用模式为

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

PatternLayoutEncoder

进行格式化。

root

logger

默认级别是

DEBUG

官方推荐

官方推荐使用的

xml

名字的格式为:

logback-spring.xml

而不是

logback.xml

,因为带

spring

后缀的可以使用

<springProfile>

这个标签。

Logback配置文件的基本结构

<configuration>

开头,后面有任意个

<appender>

元素,有任意个

<logger>

元素,有最多一个

<root>

元素。

Logback

配置文件的语法非常灵活。正因为灵活,所以无法用

DTD XML schema

进行定义。

1、根节点

<configuration>

,包含下面三个属性:

scan

: 当此属性设置为

true

时,配置文件如果发生改变,将会被重新加载,默认值为

true

scanPeriod

: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当

scan

true

时,此属性生效。默认的时间间隔为1分钟。

debug

true

时,将打印出

logback

内部日志信息,实时查看

logback

运行状态。默认值为

false

<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
  <!--省略其他配置--> 
</configuration>
           

2.子节点,分别有<appender>,<root>,<logger>,<property>,<contextName>,<conversionRule>,<springProfile>

1.子节点

<appender>

:Logback将执行日志事件输出的组件称为Appender,实现的

Appender

必须继承

ch.qos.logback.core.Appender

接口

它有两个必要属性

name

class

name

指定

appender

名称,

class

appender

的全限定名

注:

class="ch.qos.logback.core.rolling.RollingFileAppender"

常见的日志输出到文件,随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。

RollingFileAppender

用于切分文件日志

<appender>

有四个子节点

<encoder>

:对日志进行格式化。必须指定,否则不会往文件输出内容

%d{HH: mm:ss.SSS}

——日志输出时间

%thread

——输出日志的进程名字,这在Web应用以及异步任务处理中很有用

%-5level

——日志级别,并且使用5个字符靠左对齐

%logger{36}

——日志输出者的名字

%msg

——日志消息

%n

——平台的换行符

<rollingPolicy>

:循环政策:基于时间创建日志文件。当发生日志切换时,

RollingFileAppender

的切换行为。例如日志文件名的修改

常用子节点

<maxHistory>

表示只保留最近天数的日志,最好设置下,以防止日志填满整个磁盘空间。

<timeBasedFileNamingAndTriggeringPolicy>

<maxFileSize>

是对日志大小进行切割,设置每个日志文件的最大值

<filter>

:过滤器,执行一个过滤器会有返回个枚举值。

返回

DENY

,日志将立即被抛弃不再经过其他过滤器;

NEUTRAL

,有序列表里的下个过滤器过接着处理日志;

ACCEPT

,日志会被立即处理,不再经过剩余过滤器。

<Appender>

添加一个或多个过滤器后,可以用任意条件对日志进行过滤。

<Appender>

有多个过滤器时,按照配置顺序执行。

最常用的过滤器

LevelFilter

: 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据

onMath

onMismatch

接收或拒绝日志。

有以下子节点:

<level>

:设置过滤级别

<onMatch>

:用于配置符合过滤条件的操作

<onMismatch>

:用于配置不符合过滤条件的操作

<file>

:指定正在记录的日志文件的路径及文件名。

注意在

windows

当中,反斜杠

\

需要转义,或直接使用

/

也可以。例如

c:/temp/test.log

c:\\temp\\test.log

都可以。可以是相对目录,也可以是绝对目录,没有默认值,如果上层目录不存在,

FileAppender

会自动创建。

我测试时放在日志文件是放在E盘下log文件夹(目录设置下文有提到)

<appender>

节点定义示例

<!-- 时间滚动输出 level为 INFO 日志 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_info.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
           

2.子节点

<property>

:用来定义变量值,它有两个属性

name

value

,通过

<property>

定义的值会被插入到

logger

上下文中,可以使“${}”来使用变量。

<property name="log.path" value="E:/log" />
           

在上面定义文件路径中有使用到

${log.path}

3.子节点

<contextName>

:用来设置上下文名称,每个

logger

都关联到

logger

上下文,默认上下文名称为

default

。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。

<contextName>myAppLogBack</contextName> 
           

4.子节点

<logger>

:用来设置某一个包或具体的某一个类的日志打印级别、以及指定

<appender>

<logger>

仅有一个

name

属性,一个可选的

level

和一个可选的

addtivity

属性。

name

:用来指定受此

logger

约束的某一个包或者具体的某一个类。

level

:用来设置打印级别,大小写无关:

TRACE

DEBUG

INFO

WARN

ERROR

ALL

OFF

addtivity

: 是否向上级

logger

传递打印信息。默认是

true

<logger name="org.springframework.web" level="info" addtivity="true"/>
           

5.子节点

<root>

:它也是

<logger>

元素,但是它是根

logger

,是所有

<logger>

的上级。

root

节点是必选节点,用来指定最基础的日志输出级别,只有一个

level

属性,因为

name

已经被命名为

root

,且已经是最上级了。

level

:用来设置打印级别,不能设置为

INHERITED

或者同义词

NULL

。默认是

DEBUG

可以包含零个或多个元素,标识这个

appender

将会添加到这个

logger

<root level="all">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="DEBUG_FILE" />
    <appender-ref ref="INFO_FILE" />
    <appender-ref ref="WARN_FILE" />
    <appender-ref ref="ERROR_FILE" />
</root>
           

6.子节点

<springProfile>

:

Spring profile

Spring 3

引入的概念,主要用在项目多环境运行的情况下,通过激活方式实现多环境切换,省去多环境切换时配置参数和文件的修改,相比较

Maven profile

简单实用,易于上手。并且

Spring profile

提供了多种激活方法,例如配置文件,注解,

jvm

参数设置等等

据不同环境(

prod

:生产环境,

test

:测试环境,

dev

:开发环境)来定义不同的日志输出,在

logback-spring.xml

中使用

springProfile

节点来定义,方法如下:

<!-- 测试环境+开发环境. 多个使用逗号隔开. -->
<springProfile name="test,dev">
    <logger name="com.example.springcloudeureka" level="info" />
</springProfile>
<!-- 生产环境. -->
<springProfile name="prod">
    <logger name="com.example.springcloudeureka" level="ERROR" />
</springProfile>
           

可以启动服务的时候指定

profile

,springboot的话可以在配置文件

yaml

中设置

spring.profiles.active

7.子节点

<conversionRule>

:logback 自定义Pattern模板

例如需要在每条日志都输出

logback output

字符串,可以这样做

写一个转换器类,继承

ClassicConvert

public class LogbackConvert extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent iLoggingEvent) {
        return "logback output";
    }
}
           

实现里面的方法,返回的都是

String

类型

logback

配置文件中中注册该转换器,并自定义转换符

<conversionRule conversionWord="logback" 
converterClass="com.example.springcloudeureka.LogbackConvert"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" > 
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %logback %n</pattern>
</appender>
           

<appender>

中自定义

Pattern

模板,用

%转换器名

即可

效果

都打印出

logback output

字符串

Apache Commons Logging

官方地址:

https://commons.apache.org/proper/commons-logging/

Jakarta Commons-logging(JCL)是

apache

最早提供的日志的门面接口。提供简单的日志实现以及日志解耦功能。

commons-logging

Apache commons

类库中的一员。

Apache commons

类库是一个通用的类库,提供了基础的功能,比如说

commons-fileupload

commons-httpclient

commons-io

commons-codes

等。

commons-logging

能够选择使用

Log4j

还是

JDK Logging

,但是不依赖

Log4j

JDK Logging

API

。如果项目的

classpath

中包含了

log4j

的类库,就会使用

log4j

,否则就使用

JDK Logging

。使用

commons-logging

能够灵活的选择使用那些日志方式,而且不需要修改源代码。

不过现在

Apache Commons Logging

也不更新了,最新的依赖(Jul 05, 2014)

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
           

具体实现

JCL有两个基本的抽象类:Log( 基本记录器 ) 和 LogFactory( 负责创建 Log 实例 )

public class SpringbootApplication {
    public static Log LOG= LogFactory.getLog(SpringbootApplication.class);
    public static void main(String[] args) {
        LOG.debug("debug()...");
        LOG.info("info()...");
        LOG.error("error()...");
    }
}
           

控制台显示

SpringBoot已经默认集成了

log4j

关于commons-logging日志解耦可以参考文章:

https://blog.csdn.net/sakurainluojia/article/details/53534949

总结:

commons-logging

slf4j

都是日志的接口,供用户使用,而没有提供实现。

log4j

logback

等才是日志的真正实现,日志是接口+具体实现的方式来使用。

目前应用比较广泛的是

Log4j2

logback

,而

logback

作为后起之秀,以替代

log4j

为目的,整体性能比

log4j

较佳,

log4j

log4j2

也是有诸多亮点

选择使用

logback

Spring Boot

默认的日志系统,假如对日志没有特殊要求,可以完全零配置(当然也可以自定义

logback-spring.xml

)使用

SLF4J(Simple Logging Facade For Java)

logback

来输出日志。

个人推荐使用

log4j2

Log4j2

Log4j

的升级版,与之前的版本

Log4j 1.x

相比、有重大的改进,在修正了

Logback

固有的架构问题的同时,改进了许多

Logback

所具有的功能。关于

Log4j2

的新特性可以在其官网首页查看

关于

log4j2

的性能使用可以参考

https://www.jianshu.com/p/570b406bddcd https://blog.csdn.net/u011054333/article/details/54412360 https://blog.csdn.net/yjh1271845364/article/details/70888262