首页 > 其他分享 >日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

时间:2023-12-28 09:44:16浏览次数:42  
标签:Logging LOG Slf4j 日志 Logback Log4j

前言

随着互联网和大数据的迅猛发展,分布式日志系统和日志分析系统已广泛应用,几乎所有应用程序都使用各种日志框架记录程序运行信息。因此,作为工程师,了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影响,但没有日志的应用程序是不完整的,甚至可以说是有缺陷的。优秀的日志系统可以记录操作轨迹监控系统运行状态解决系统故障


 

Java 日志框架进化史

早期 Java 日志框架没有制定统一的标准,使得很多应用程序会同时使用多种日志框架。Java 日志框架的发展历程大致可分为以下几个阶段:

 

 

 

1.Log4j:Apache Log4j是一种基于Java的日志记录工具。该项目由Ceki Gülcü于1999年创建,并几乎成为了Java日志框架的实际标准。 2.JUL:Apache 希望将 Log4j 引入 jdk,不过被 sun 公司拒绝了。随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL(java.util.logging)。 3.Commons Logging:为了解耦日志接口与实现,Apache在2002年推出了JCL(Jakarta Commons Logging)。JCL定义了一套日志接口,具体的实现由Log4j或JUL完成。Commons Logging使用动态绑定来实现日志记录,编码时只需要使用它定义的接口即可,程序运行时会使用ClassLoader来查找和加载底层的日志库,因此可以灵活选择Log4j或JUL来实现日志功能。 4.Slf4j&Logback:Ceki Gülcü与Apache基金会在Commons-Logging标准上存在分歧。后来,Ceki Gülcü离开了Apache,并创建了Slf4j和Logback两个项目。Slf4j是一个日志门面,仅提供接口,可以支持Logback、JUL、log4j等日志实现。而Logback则提供了具体的实现。相比于log4j,Logback具有更快的执行速度和更完善的功能。 5.Log4j 2:为了保持在Java日志领域的地位,防止JCL和Log4j被Slf4j和Logback取代,Apache在2014年推出了Log4j 2。Log4j 2与log4j不兼容,经过大量深度优化,其性能得到显著提升。
 

日志框架介绍

在上文中已经提及,目前常用的日志框架有 Log4j,Log4j 2,Commons Logging,Slf4j,Logback,JUL。这些日志框架可以分为两种类型:门面日志和日志系统。

日志门面

日志门面(Logging Facade)是一种设计模式,用于在应用程序中实现日志记录的抽象层。它提供了一组统一的接口和方法,即相应的 API,而不提供具体的接口实现。日志门面在使用时,可以动态或者静态地指定具体的日志框架实现,解除了接口和实现的耦合,使用户可以灵活地选择日志的具体实现框架。

日志系统

日志系统(Logging System)是指用于记录和管理应用程序运行时产生的日志信息的软件工具或框架。与日志门面相对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的功能,如日志级别管理、日志格式化、日志输出目标设置等。常见的日志系统包括Log4j、Logback、Java Util Logging等。

 

 

 

通过使用日志门面,我们可以在应用程序中使用统一的API进行日志记录,而具体的日志实现可以根据需要选择和配置。这样,我们可以根据项目需求和团队喜好来灵活选择、切换和配置日志系统,而不会对应用程序代码造成太大影响。

避免环形依赖

Slf4j 的作者 Ceki Gülcü 当年因为觉得 Commons-Logging 的 API 设计的不好,性能也不够高,因而设计了 Slf4j。而他为了 Slf4j 能够兼容各种类型的日志系统实现,还设计了相当多的 adapter 和 bridge 来连接,如下图所示:

 

 

 

鉴于此,在引入日志框架依赖的时候要尽力避免,比如以下组合就不能同时出现:

•jcl-over-slf4j 和 slf4j-jcl •log4j-over-slf4j 和 slf4j-log4j12 •jul-to-slf4j 和 slf4j-jdk14

日志框架的使用选择

常用的组合使用方式是 Slf4j & Logback 组合使用,Commons Logging & Log4j 组合使用。

推荐

Slf4j & Logback

原因

1. Slf4j 实现机制决定 Slf4j 限制较少,使用范围更广。相较于 Commons-Logging,Slf4j 在编译期间便静态绑定本地的 Log 库,其通用性要好得多;

2. Logback 拥有更好的性能。Logback 声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高,这个操作在 Logback 中只需 3 ns,而在 Log4j 则需要 30 ns;

3. Slf4j 支持参数化,使用占位符号,代码更为简洁,如下例子:

// 在使用 Commons-Logging 时,通常的做法是 
if(log.isDebugEnabled()){ 
    log.debug("User name: " + user.getName() + " buy goods id :" + good.getId()); 
} 

// 在 Slf4j 阵营,你只需这么做: 
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());

4. Logback 的所有文档是免费提供的,Log4j 只提供部分免费文档而需要用户去购买付费文档;

5. MDC (Mapped Diagnostic Contexts) 用 Filter,将当前用户名等业务信息放入 MDC 中,在日志 format 定义中即可使用该变量。具体而言,在诊断问题时,通常需要打出日志。如果使用 Log4j,则只能降低日志级别,但是这样会打出大量的日志,影响应用性能;如果使用 Logback,保持原定日志级别而过滤某种特殊情况,如 Alice 这个用户登录,日志将打在 DEBUG 级别而其它用户可以继续打在 WARN 级别。实现这个功能只需加 4 行 XML 配置;

6. 自动压缩日志。RollingFileAppender 在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩过程是异步的,因此在压缩过程中应用几乎不会受影响。


 

Slf4j+Logback入门实践

maven依赖

pom.xml

<!--日志框架接口-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<!--日志框架接口实现-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>
<!--日志框架核心组件-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
</dependency>

<!--自动化注解工具-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>

配置文件

logback.xml

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

    <!--默认日志配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 控制台日志 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="UTF-8">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Info日志 -->
    <appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-info.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>NEUTRAL</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 -->
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 -->
            <totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 -->
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Warn日志 -->
    <appender name="FILE-WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-warn.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 -->
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 -->
            <totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 -->
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Error日志 -->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-error.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="info-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-INFO"/>
        <queueSize>512</queueSize> <!-- 异步队列的大小 -->
    </appender>
    <appender name="warn-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-WARN"/>
        <queueSize>512</queueSize> <!-- 异步队列的大小 -->
    </appender>
    <appender name="error-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-ERROR"/>
        <queueSize>512</queueSize>
    </appender>

    <!-- 应用日志 -->
    <logger name="com.improve.fuqige.bronze" additivity="false">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE-INFO"/>
        <appender-ref ref="FILE-WARN"/>
        <appender-ref ref="FILE-ERROR"/>
    </logger>


    <!-- 总日志出口 -->
    <root level="${logging.level.root}">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="info-asyn"/>
        <appender-ref ref="warn-asyn"/>
        <appender-ref ref="error-asyn"/>
    </root>
</configuration>

applicantion.properties

logging.file=fuqige-bronze
logging.path=XXXXXX/Logs/XXXXXX
logging.level.root=info
logging.level.com.improve.fuqige.bronze=info
logging.pattern.console=%cyan(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow([%thread]) %highlight(%-5level) %boldGreen(%logger{80}[LineNumber:%L]): %highlight(%msg%n)
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] %-5level --- [%thread] %logger{80}[LineNumber:%L]: %msg%n

测试用例

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        log.info("进来了!");
        log.warn("进来了!");
        log.error("进来了!");
        return "hello, world! requestId=" + MDC.get("requestId");
    }
}

 


 

参考资料

Java 日志框架: https://zhuanlan.zhihu.com/p/365154773

SLF4J框架常见的用法和最佳实践: https://juejin.cn/post/7215569601161166906

作者:京东零售 张洪

来源:京东云开发者社区 转载请注明来源

标签:Logging,LOG,Slf4j,日志,Logback,Log4j
From: https://www.cnblogs.com/Jcloud/p/17932008.html

相关文章

  • ETCD 下线Member未剔除引发的日志报错
    背景介绍容器化的etcd集群原来具有三个节点分别为etcd-0,etcd-1,etcd-2,在节点etcd-2下线后剩两个节点etcd-0,etcd-1#kubectlgetpod-napisixNAMEREADYSTATUSRESTARTSAGEetcd-0......
  • MySQL 事务日志
    MySQL事务日志事务有4种特性:原子性,一致性,隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢(是通过什么来控制的呢)?事务的"隔离性"由锁机制实现(通过加锁来实现隔离)。而事务的"原子性","一致性"和"持久性"由事务的redo日志和undo 日志来保证redolog称......
  • C# 如何设计一个好用的日志库?【架构篇】
    C#如何设计一个好用的日志库?【架构篇】 阅读目录〇、前言一、日志的简单记录二、通过开源库HslCommunication记录不同级别的日志三、通过开源库NLog实现通过配置文件配置日志选项1.配置文件2.测试代码3.日志记录类四、日志查看器TextAnalysisTool.NET......
  • nginx切割日志部署脚本编写
    #!/bin/bash#utf-8#description:部署nginx_lograte.sh脚本#---------------------------------------------------------------------script_name="logrotate_new.sh"script_download_directory="http://172.20.147.61/CentOS/app/script/hby"#......
  • 12-Mysql的日志管理
    一、mysql常见日志二、错误日志配置方法:[mysqld]log-error=/data/mysql/mysql.log查看配置方式:mysql>showvariableslike'%log%error%';作用:记录mysql数据库的一般状态信息及报错信息,是我们对于数据库常规报错处理的常用日志,默认在data目录下三、一般查询......
  • 事件时间日志,精准工作之道
    你好,我是刚哥。事件时间日志,是在李笑来《把时间当做朋友》中看到的,来源于柳比歇夫的日志,Event-timeLog。事件日志只记录事件的名称,而事件时间日志还记录了时间,比如:给斯拉瓦写信——二小时四十五分。李笑来在书中谈到了事件时间日志的好处:“它会使你对时间的感觉越来越精确。可......
  • python之日志logging
    转载:https://blog.csdn.net/weixin_43790276/article/details/101944628 logging模块是Python内置的标准模块,用于输出代码日志。一、logging模块简介在工作中,运行的代码量是非常大的,为了更方便的管理代码运行,监控代码运行的过程,需要在代码中添加一些必要的日志输出。Py......
  • 自定义php日志类收集应用日志
    <?phpclassMiniLog{ privatestatic$_instance; private$_path; private$_pid; private$_handleArr; function__construct($path){ $this->_path=$path; $this->_pid=getmypid(); } privatefunction__clone(){ } publics......
  • Windows事件日志
    本文主旨:了解Windows事件日志的相关信息,以便于后续进行Windows日志分析。事件日志简介事件日志记录[1]为(的应用程序和操作系统)记录重要软件和硬件事件提供了一种标准的集中式方法。事件日志记录服务记录来自各种源的事件,并将其存储在名为事件日志的单个集合中。事件日志......
  • rabbitMq怎么查看队列日志消息-Tracing日志
    Trace是Rabbitmq用于记录每一次发送的消息,方便使用Rabbitmq的开发者调试、排错。1、启动Tracing插件在RabbitMQ中默认是关闭的,需手动开启。此处rabbitMQ是使用docker部署的##进入rabbitMq中dockerexec-itrabbitmq1bash##启动日志插件rabbitmq-pluginsenablerabbitmq_tr......