Java 日志系统

429 阅读5分钟

文章主题

本篇文章主要包含以下部分:

  1. 什么是日志
  2. 日志的级别
  3. System 与 Log 的区别
  4. 常见的日志框架
  5. LogBack 配置

什么是日志

1、日志从字面上理解就是我们对做的某一件事情的过程进行记录,方便我们之后进行事件回顾或者说问题排查的手段。

2、在 Java 中,日志是用于记录程序运行时的信息的重要工具。方便我们对于执行请求时的 数据变更 & 请求情况的记录。

日志的级别

  • TRACE:最详细的日志级别,用于追踪程序的执行流程和细节,一般在开发和调试阶段使用,在生产环境中很少使用。
  • DEBUG:用于调试目的,包含更详细的程序内部信息,有助于开发者理解程序的运行逻辑和查找问题。
  • INFO:记录一般的重要信息,例如程序的启动、配置加载、关键操作的执行等。
  • WARN:表示可能出现的问题或不太正常但不影响程序正常运行的情况,需要引起关注。
  • ERROR:表示出现了错误,影响了程序的部分功能,但程序仍能继续运行。
  • FATAL:表示非常严重的错误,导致程序无法继续运行。

级别排序:FATAL > ERROR > WARN > INFO > DEBUG > TRACE

在实际应用中,可以根据不同的环境(开发、测试、生产)灵活配置日志级别,以便在不影响性能的前提下获取到有价值的信息。例如,在生产环境中,通常只记录 INFO 及以上级别的日志,而在开发和测试环境中,可以启用更低级别的日志来辅助调试。

System 与 Log 的区别

对于我们的大多数同学来说接触 Java 所认识的最早的关键字肯定是包含 System 的因为我们学一门语言基本都是从 Hello World 开始的。

System 实际上做的一个工作也是一个涉及到 IO 流的相关操作,大致是通过 Input IO 操作把你想要展示的数据输出到控制台文件,这里既然涉及到 IO 相关的操作所以会涉及到安全问题所以 System.out.println println 这个函数添加了相关的 synchronized 关键字来对输入对象进行加锁操作保证线程安全。

System 的优缺点

缺点:

  • 同步操作:他是同步的,这意味着在多线程环境中,可能会产生线程阻塞和竞争,影响性能。
  • 无缓冲:每次调用都会立即进行输出操作,没有缓冲区来优化 I/O 性能

优点:

  • 使用方便:无须接入任何的外部依赖即可使用,通常用于写 demo 中

Log 框架

Log 的优缺点

优点:

  • 可配置: 可以根据不同的环境 (开发、测试、UAT、生产) 来设置不同的日志级别和输出方式,从而在非必要时减少日志输出,提高效率。
  • 缓存机制:通常具有缓冲机制,将多个日志消息累计到一定数量或一定时间后再进行输出,减少频繁的 I/O 操作 (BufferWriter 缓冲区 + 结合 Disruptor 队列进行异步调度任务分配实现解耦 + 异步操作 提高系统性能)。
  • 门面设计:对于 Java 现在的流行日志框架基本都是基于 SL4J 进行门面设计,我们基本可以 0 成本进行切换。

缺点:

需要接入额外的依赖

常见的日志框架

  • Log4j:这是一个非常经典且广泛使用的 Java 日志框架。它提供了灵活的配置选项,可以将日志输出到不同的目的地,如控制台、文件、数据库等。
  • Log4j2:是 Log4j 的升级版本,性能有了显著提升,具有更强大的功能和更灵活的配置方式。
  • Slf4j:简单日志门面(Simple Logging Facade for Java),它并不是一个具体的日志实现框架,而是提供了一套统一的日志接口,允许在运行时绑定不同的具体日志实现框架。
  • Java Util Logging:Java 自带的日志框架,但功能相对较简单。
  • Logback:被认为是 Log4j 的继承者,具有高效的性能和丰富的特性。

LogBack 配置

主要包含以下及部分:

  • 标签定义属性
  • 输出标签
    • 定义过滤日志级别
    • 定义 编解码
    • 定义日志滚动器
  • 定义日志环境
  • 定义日志级别的具体输出
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <contextName>shi-yan-app-transport-business-all</contextName>
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="log.path" value="./logs" />
    <property name="app.name" value="eric" />
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 2.2 level为 INFO 日志,时间滚动输出  -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${app.name}-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}/${app.name}/${app.name}-info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>20</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.3 level为 WARN 日志,时间滚动输出  -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${app.name}-warn.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}/${app.name}/${app.name}-warn-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>20</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录warn级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.4 level为 ERROR 日志,时间滚动输出  -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/${app.name}-error.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}/${app.name}/${app.name}-error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>20</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--
    <logger name="org.springframework.web" level="WARN"/>
    <logger name="org.springframework.boot" level="WARN" />
    <logger name="springfox.documentation.spring" level="WARN" />
    <logger name="org.apache.shardingsphere" level="DEBUG" />
  -->
    <!-- 开发、测试环境 -->
    <springProfile name="dev,sit,test,uat">
        <logger name="org.springframework.web" level="INFO"/>
        <logger name="org.springframework.boot" level="INFO" />
        <logger name="springfox.documentation.spring" level="INFO" />
        <logger name="ShardingSphere-SQL" level="INFO" />
        <logger name="SQLStatement" level="INFO" />
        <logger name="com.alibaba.nacos.client.naming" level="WARN" />
        <logger name="org.apache.shardingsphere.core.route.SQLLogger" level="INFO" />
        <logger name="com.zkj.lawfirm.platform" level="INFO" />
    </springProfile>

    <!-- 生产环境-->
    <springProfile name="prod">
        <logger name="org.springframework.web" level="WARN"/>
        <logger name="org.springframework.boot" level="WARN" />
        <logger name="springfox.documentation.spring" level="WARN" />
        <logger name="org.apache.shardingsphere" level="WARN" />
        <logger name="ShardingSphere-SQL" level="WARN" />
        <logger name="com.alibaba.nacos.client.naming" level="WARN" />
        <logger name="org.apache.shardingsphere.core.route.SQLLogger" level="WARN" />
        <logger name="com.zkj.lawfirm.platform" level="INFO" />
    </springProfile>

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

</configuration>