文章主题
本篇文章主要包含以下部分:
- 什么是日志
- 日志的级别
- System 与 Log 的区别
- 常见的日志框架
- 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>