$$ SLF4J 是一个接口,它本身不具有输出日志的功能,输出日志还是由抽象类来实现【比如 log4j,logback】【类比 JDBC 只是一种规则】 $$
❤️ 概述
介绍
我们在编写代码的时候,只会使用 SLF4J 里的 API,应用程序在运行时再去类路径下查找绑定的具体日志实现,并使用其进行实际的日志操作【如果在应用程序的类路径下面没有找到合适的绑定的话,默认无操作】,也就是说,你要使用日志,你要引入两个依赖【slf4j-api,实现类 api】,但是 SpringBoot-Starter-Web 里已经有了 logback 依赖,所以我们还是只用引入 slf4j 就好了
[!hint] 有的时候可以使用 日志打印 代替 注解
[!hint] 为什么要选择抽象层的 SLF4J,而不是实现类【log4j,logback】? 假设 A 开发了一个通用组件,他在程序中使用的是 log4j,B 之前开发的的业务模块使用的是 logback。突然有一天 B 要在自己的业务系统中使用 A 的通用组件,那就很麻烦了,那么解决方案就是使用 SLF4J
日志级别
fatal灾难级的,因为代码异常导致程序退出执行的事件;系统级别,程序无法打印error错误信息,可以快速打印出格式化的堆栈信息warn警告信息info普通的打印信息debug需要调试时候的关键信息trace级别最低
[!hint] 当某个项目目录设置了日志级别,我们只能得到
此级别及更高级别的日志,SpringBoot 的默认级别是info
日志格式
日志 = 日志打印时间 + 日志级别 + 线程 id + 线程名称 + 日志所在类 + 日志内容
❤️ 配置
我们可以使用两种方式来配置 SLF4J:
- 直接在 yml 配置文件中配置 :方便
- 定义
logback-spring.xml,再在 yml 配置文件中激活logback-spring.xml:功能更强大,可以对不同环境下【开发环境,测试环境,生产环境……】进行日志配置
直接在 yml 配置文件中配置
logging:
# 指定不同包下使用不同的日志级别
level:
root: INFO # 设置全局日志级别
app.xlog.ggbond: INFO # 设置包级别日志级别
org.springframework: WARN # 设置spring的日志级别
# 以文件形式打印日志logging.file
file:
name: D:/boot.log #指定日志文件的具体位置
#日志输出格式:控制台,文件
pattern:
console: %d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
file: %d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
# %d 表示日期时间
# %thread 表示线程名
# %-5level 表示日志级别的显示宽度是5【无论实际的日志级别是什么,它都会在控制台上占用 5 个字符的宽度】,为了使得不同的日志级别在控制台上更加整齐
# %logger{50} 表示logger名字最长50个字符,否则按照句点分割
# %msg 日志消息
# %n 换行符使用 logback-spring.xml 配置
添加日志配置文件 logback-spring.xml,在 logback-spring.xml 中,你可以定义多个 Spring Profile
- 在 yml 配置文件中激活
Spring Profile
# 激活了名为"dev"的Spring Profile
spring:
profiles:
active: dev- 在
logback-spring.xml中指定Spring Profile
<configuration>
<!-- 定义第一个Spring Profile文件 -->
<springProfile name="dev">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE" />
</root>
</springProfile>
<!-- 定义第二个Spring Profile文件 -->
<springProfile name="!dev">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<file>logs/app.log</file>
</appender>
<root level="info">
<appender-ref ref="FILE" />
</root>
</springProfile>
</configuration>❤️ 切换日志框架
SpringBoot 默认使用 spring-boot-starter-logging 启动器【Logback 的启动器】, 如果要切换成 Log4j2 进行日志记录,那就要切换成 spring-boot-starter-log4j2 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除默认spring-boot-starter-logging启动器-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--使用spring-boot-starter-log4j2启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>❤️ 具体操作
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.12</version>
</dependency>基本操作
- 手动创建
Logger实例 :更灵活地控制Logger的创建和使用
@SpringBootTest
class Web2ApplicationTests {
@Test
public void test() {
// 使用 LoggerFactory 创建与该类名相同的记录器
Logger logger = LoggerFactory.getLogger(Web2ApplicationTests.class);
logger.info("Hello SLF4J !");
// 使用占位符
String s = "greenteck";
String ss = "pop";
logger.info("Hello {}!, {}", s, ss);
}
}
---
2024-04-10T11:01:45.273+08:00 INFO 14720 --- [ main] com.example.web_2.Web2ApplicationTests : Hello SLF4J !
2024-04-10T11:01:45.273+08:00 INFO 14720 --- [ main] com.example.web_2.Web2ApplicationTests : Hello greenteck!, pop- 使用
@Slf4j注解 :简洁
@Slf4j
public class StrategyArmoryDispatch {
public void assembleLotteryStrategyRuleCommon(Integer strategyId) {
log.atInfo().log("装配策略{}的全奖品完成", strategyId);
}
}链式编程记录日志
[!hint] 这种方式可以使用
addKeyValue(key, value)给日志添加键值对,更有利于后续分析
atTrace(),atDebug(),atInfo(),atWarn() ,atError(),atFatal() 方法都会返回一个 LoggingEventBuilder 实例
@SpringBootTest
class Web2ApplicationTests {
@Test
public void test() {
Logger logger = LoggerFactory.getLogger(Web2ApplicationTests.class);
logger.atInfo().log("Hello");
String oldT = "1";
String newT = "3";
logger.info("oldT={} newT={} Temperature changed.", oldT, newT);
logger.atInfo().setMessage("Temperature changed.").addKeyValue("oldT", oldT).addKeyValue("newT", newT).log();
}
}
---
2024-04-10T11:14:51.204+08:00 INFO 10180 --- [ main] com.example.web_2.Web2ApplicationTests : Hello自定义 appender
ILoggingEvent 的方法 :
String getFormattedMessage()返回格式化后的日志消息内容String getMessage()返回未格式化的日志消息内容Level getLevel()返回日志的级别(例如 DEBUG, INFO, WARN, ERROR 等)long getTimeStamp()返回日志生成的时间戳(以毫秒为单位)String getLoggerName()返回记录日志的日志器(Logger)的名称String getThreadName()返回记录日志的线程名称Map<String, String> getMDCPropertyMap()返回上下文中存储的 MDC(Mapped Diagnostic Context)键值对StackTraceElement[] getCallerData()返回调用日志记录的堆栈信息数组,通常包含类名、方法名、行号等Object[] getArgumentArray()返回与日志消息一起传递的参数数组boolean hasCallerData()判断是否包含调用者数据(调用类、方法、行号等)IThrowableProxy getThrowableProxy()如果日志记录时有异常抛出,则返回异常对象的代理
在 logback 中 :
- 创建 Appender 类
public class CustomAppender extends AppenderBase<ILoggingEvent> {
@Override
protected void append(ILoggingEvent eventObject) {
// 获取日志信息
String message = eventObject.getFormattedMessage();
// 自定义处理逻辑,比如将日志输出到控制台或保存到文件
System.out.println("CustomAppender - Log: " + message);
}
}- 配置自定义的 appender
@Configuration
public class LogbackConfig {
@PostConstruct
public void init() {
// 获取 Logback 上下文
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
context.reset(); // 清空现有配置
// 1. 创建自定义 Appender
ZakiAppender zakiAppender = new ZakiAppender();
zakiAppender.setContext(context);
zakiAppender.start();
// 3. 将 Appender 绑定到 Root Logger
Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.addAppender(zakiAppender);
}
}