NET2Java之五:日志记录

由于更早的拥抱开源,Java在解决方案的数量上拥有.NET无法比拟的优势,可这就苦了我们这样的菜鸡玩家,比如日志,光主流的解决方案就有JUL、Log4j、Logback、Log4j2、SLF4j,这么多方案,到底要用哪一个?

若是论资历的话,log4j无疑是老前辈,作为早期使用最广的的日志框架,由Ceki创建,后来成为Apache的顶级项目之一,说是Java社区的日志标准也不为过,不过Sun公司可不这么觉得,觉得Apache就是典型的关公面前耍大刀,于是在jdk1.4之后增加了一个名为“java.util.logging”的包,简称JUL,用于日志记录。不过由于log4j已经有了广大的群众基础,加上JUL时间仓促,有些地方并不好用,所以不少人对此并不买账。Apache一瞅,Sun也太小家子器了,决定教教Sun什么叫格局,就推出了Jakarta Commons Logging,简称JCL,JCL定义了一套日志接口,只要你调用的JCL的接口,你想用log4j用log4j,想用jul用jul,我全都要。据说是受不了Apache的工作氛围,log4j的创始人Ceki于2006年出走Apache,并先后创建了Slf4j(类似于JCL)和logback(类似于log4j),于是Java日志领域一分为二:JCL和Slf4j两大阵营。logback作为后起之秀,船小好调头,又有亲爹加持,发展的越来越快,隐隐有些压住log4j一头,所以Apache在2012年又在log4j的基础上升级为2.0版本(赛亚人变身了属于),也就是现在的log4j2。

其实说白了就是天才老爹入赘豪门,生了个哥哥,后来名门大户的呆不习惯,离家创业,又生了个弟弟,亲爹肯定是想弟弟继承家业,但是大户族老们可不答应,毕竟宝都押在了哥哥头上,于是上演了一出兄弟相争的戏码,倒霉的却是嫡出的JUL。

slf4j

官网:https://slf4j.org/

sfl4j全称Simple Logging Facade for Java,它为各种日志框架(log4j、logback等)提供了一个共同的API,而非日志的具体实现方案,所以如果需要实现日志的输出,还需要引入其他框架。由于slf4j将应用程序和具体的日志实现方案解耦,可以让开发人员在不同的日志框架之间进行切换,比如你开始用了log4j后来发现logback更好用,只需要修改依赖和配置即可,而无需修改代码。所以在开发时,我们在项目中要避免直接使用log4j和logback中的API,而应该依赖使用slf4j中API。如果使用了slf4j,那么无论你使用log4j还是logback,都可以使用下面的代码记录日志:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    public static void main(String[] args) {
        System.out.println("开始执行");

        Logger logger = LoggerFactory.getLogger(Main.class);

        logger.trace("跟踪日志");
        logger.debug("调试日志");
        logger.info("普通日志");
        logger.warn("报警日志");
        logger.error("错误日志");

        System.out.println("执行完毕");
    }
}

Maven依赖:

<dependency> 
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>2.0.10</version>
</dependency>

slf4j+log4j2

官网:https://logging.apache.org/log4j/2.x/index.html

在应用中想使用,slf4j+log4j来记录日志,除了需要使用slf4j-api之外,还需要额外引入适配器:

  • slf4j 1.7.x及其之前的版本使用log4j-slf4j-impl
  • slf4j 2.0.x及其之后的版本使用log4j-slf4j2-impl

项目依赖:

<dependencies>
    <!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version> 
    </dependency>
    
    <!-- Log4j2 API -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.1</version>
    </dependency>
    
    <!-- Log4j2 Core Implementation -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version> 
    </dependency>
    
    <!-- SLF4J Log4j2 Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>

不过一般情况下由于log4j-corelog4j-slf4j-impl中已经自动依赖了log4j-apislf4j-api,所以有时候依赖会简写为:

<dependencies>        
    <!-- Log4j2 Core Implementation -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version> 
    </dependency>
    
    <!-- SLF4J Log4j2 Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>

如果你需要使用log4j 1.x,那么可以使用log4j-to-slf4j用于log4j 1.x和slf4j之间桥接。

log4j的配置可通过编程方式和配置文件的方式实现,主流的方式都是使用配置文件:

  • log4j 2配置文件的名称为log4j2.mxl,放在src目录下,maven项目放在resources下。
  • log4j 2也支持json、yaml等格式的配置文件,如log4j2.properties、log4j2.yaml、log4j2.json等

在resources下面新建log4j2.xml文件。

<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="error">
    <!--定义变量-->
    <Properties>
        <!--定义日志存储目录的变量-->
        <Property name="logDir">D:/logs</Property>
    </Properties>
    <!--输出源-->
    <Appenders>
        <!--输出到控制台-->
        <Console name="Console" target="SYSTEM_OUT">
            <!--可选配置:控制台只输出level及以上级别的信息,其他的直接拒绝,具体的可以查看ThresholdFilter相关的说明-->
            <ThresholdFilter level="INFO" onMatch="ACCEPT"/>
            <!--日志的输出格式-->
            <PatternLayout pattern="[%-level]%d{yyyy-MM-dd HH:mm:ss.SSS} %logger{36} - %msg%n"/>
        </Console>
        <!--输出到文件,大部分时候都会使用RollingFile-->
        <File name="File" fileName="${logDir}/log.txt">
            <PatternLayout>
                <Pattern>%d %p %c [%t] %m%n</Pattern>
            </PatternLayout>
        </File>

        <!-- 滚动输出:
            name:Appender的唯一标识,跟下面AppenderRef中的ref对应。
            fileName:指定了日志文件当前活动路径和文件名,决定了日志文件被写入的初始文件位置。
            filePattern:定义滚动后的文件命名规则
         -->
        <RollingFile name="RollingFileInfo" fileName="d:/logs/log.txt" filePattern="d:/logs/log-%d{yyyy-MM-dd}_%i.txt">
            <!-- 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 输出的格式  -->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss,SSS}] [%p] - %l - %m%n"/>
            <!-- Policies:滚动策略,决定何时应该产生新日志 -->
            <Policies>
                <!--时间策略:具体的看文件命名格则,如{yyyy-MM-dd},那这个意思就是每天一个,如果是{yyyy-MM-dd HH}那这个意思就是每小时,以此类推-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--大小策略:每当文件大小达到500MB,就开始滚动,产生一个新日志文件,后面的%i即标识产生了第几个文件-->
                <SizeBasedTriggeringPolicy size="500MB"/>
            </Policies>
            <!--文件夹下最多可以有几个文件,过期的旧文件会被删除,默认是7-->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>
    <!-- 记录器 -->
    <Loggers>
        <!--
         name:指定logger对象的名称,用于区别日志记录的来源
         level:debug、info、warn、error、fatal,按重要程度从低到高,如果配置为warn,那么就只有warn、error、fatal的日志会被记录
         additivity:用于指定日志消息是否要向上传递给父级日志记录器 -->
        <Logger name="em.im.pve" level="debug" additivity="true"/>

        <!-- 日志系统中的最顶层Logger,代表了日志层次结构的根节点,指定使用哪些Appender -->
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFileInfo"/>
        </Root>
    </Loggers>
</Configuration>

Appenders中远不止控制台和文件这两种形式,还包括数据库、MQ、HTTP等多种输出形式,但业务上最常用的还是输出到文件,如果有其他的输出需求,再去官方阅读文档即可。不过我觉得对大多数人来说,困惑最多的可能还是Layout的使用,比如:[%d{yyyy-MM-dd HH:mm:ss,SSS}] [%p] - %l - %m%n这到底是个啥?且听我一一拆解:

  • %d{yyyy-MM-dd HH:mm:ss.SSS}:就是表示格式化输出的时间,如果你就是示例的输出,一个%d就行。
  • [ ]就是字面意思,不用纠结。
  • %level:日志级别和%p表示的结果一样。
  • %pid:进程id
  • %-5P:-表示左对齐,5表示占位空间
  • %L:输出行号
  • %m:日志内容
  • %n:换行,一般就是一条日志一行
  • %t:输入制表符
  • %logger:和%c的意思一样,输出日志记录器的名称,后面紧跟的数字是精度,以减小记录器名称的大小

slf4j+logback

官网:https://logback.qos.ch/

logback中有三个主要的核心模块:logback-core、logback-classic、logback-access

  • logback-core:核心模块,提供了日志框架的基础功能,是我们通常意义上说的logback。
  • logback-classic:实现了slf4j api。
  • logback-access:用于日志访问,可以通过HTTP访问日志信息。

由于logback-classic中已经引入了logbak-core和slf4j-api,所以直接引入logback-classic即可:


<dependencies>
    <!-- LogBack -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.10</version>
    </dependency>
</dependencies>

配置文件(logback.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!--配置根:
        scan:设置为true是,如果配置文件发生更改,配置就会重新加载
        scanPeriod:当scan为true时,会依据这个进行扫描查看是不是更新,默认单位是ms
-->
<configuration scan="true" scanPeriod="60 seconds">
    <!-- 变量定义 -->
    <property name="logPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level %c{36} - %msg%n"/>
    <property name="logPath" value="d:/logs/log.txt" />

    <!-- 控制台输出 -->
    <appender name="logConsole" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${logPattern}</pattern>
        </encoder>
    </appender>
    <!-- 文件输出,记录INFO级别以上的日志 -->
    <appender name="logFileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}</file>
        <encoder>
            <pattern>${logPattern}</pattern>
        </encoder>
        <!-- 滚动策略:当然还有FixedWindowRollingPolicy、SizeBasedTriggeringPolicy等策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--滚动输出的路径-->
            <fileNamePattern>log/app-%d{yyyy-MM-dd}.txt</fileNamePattern>
            <!--文件夹下保留文件的最大数量-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- 根日志级别设置为INFO,附加到控制台和文件输出 -->
    <root level="INFO">
        <appender-ref ref="logConsole"/>
        <appender-ref ref="logFileInfo"/>
    </root>
</configuration>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章