SLF4J 外观与日志记录器
在正确的级别记录日志
SLF4J 外观与日志记录器 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
什么是 SLF4J?
SLF4J(Java 的简单日志门面)是一种抽象层。您可以针对它的 API 编写代码,而由 Logback 或 Log4j2 这样的后端实际执行日志记录。
这样,库就可以记录日志,而不会强迫应用程序使用某个特定实现。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
log.info("Application started");
}
}获取日志记录器
使用 LoggerFactory.getLogger(MyClass.class) 为每个类创建一个日志记录器。
按照惯例,这个字段声明为 private static final,名称为 log 或 logger。该类会成为日志记录器的名称,并显示在输出中。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
void placeOrder(String id) {
log.info("Placing order {}", id);
}
}五个级别
SLF4J 日志记录器为每个级别提供一个方法:
log.trace(...)log.debug(...)log.info(...)log.warn(...)log.error(...)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
log.trace("entering main");
log.debug("args length = {}", args.length);
log.info("service is up");
log.warn("cache miss");
log.error("failed to connect");
}
}选择正确的级别
指导原则:
- ERROR:需要人工处理的真实故障。
- WARN:可以恢复的异常情况。
- INFO:重要的生命周期事件。
- DEBUG:供开发人员使用的诊断信息,在生产环境中关闭。
- TRACE:极其详细的跟踪信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PaymentService {
private static final Logger log = LoggerFactory.getLogger(PaymentService.class);
void charge(int cents) {
if (cents <= 0) { log.warn("non-positive charge: {}", cents); return; }
log.info("charged {} cents", cents);
}
}日志记录器名称与层级
日志记录器名称按照以点分隔的软件包构成层级结构。名为 com.example.svc 的日志记录器是 com.example 的子级。
除非被覆盖,否则对父级所做的配置会应用于子级,因此您可以按软件包设置级别。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
public static void main(String[] args) {
Logger a = LoggerFactory.getLogger("com.example.app");
Logger b = LoggerFactory.getLogger("com.example.app.db");
a.info("parent logger");
b.debug("child inherits parent's level unless overridden");
}
}保护开销较大的调用
对于开销较大的消息构造,请使用 isDebugEnabled() 进行保护,这样只有在该级别处于启用状态时才会执行相关工作。
使用参数化日志记录(下一课会介绍)时,您很少需要这样做;但如果要自行构造字符串,这一点就非常重要。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
static String heavyDump() { return "...large state..."; }
public static void main(String[] args) {
if (log.isDebugEnabled()) {
log.debug("state: {}", heavyDump());
}
}
}记录异常
若要记录带有完整堆栈跟踪的异常,请将 Throwable 作为最后一个参数传入。
不要将异常放入占位符中;请将它放在消息之后传入。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
try {
throw new IllegalStateException("boom");
} catch (Exception e) {
log.error("Operation failed for id {}", 42, e);
}
}
}每个类使用一个日志记录器
将日志记录器声明为静态最终字段,并且只声明一次。不要在方法内部创建日志记录器,因为这会浪费工作量,也会失去使用类名作为日志记录器名称的优势。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserRepository {
// Good: one shared logger named after the class
private static final Logger log = LoggerFactory.getLogger(UserRepository.class);
void save(String user) { log.debug("saving {}", user); }
}SLF4J 与后端
SLF4J 只是 API。在运行时,它会绑定到类路径上的任意后端。
- 添加
logback-classic:SLF4J 使用 Logback。 - 添加
log4j-slf4j2-impl:SLF4J 使用 Log4j2。
您的代码完全不需要修改。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
// Same code, different backend depending on classpath
log.info("backend-agnostic logging");
}
}避免记录敏感数据
绝不要记录密码、令牌或完整的信用卡号码。日志通常会被发送到多个系统,并且长期保留。
记录日志前,请遮盖或省略敏感字段。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
static String mask(String card) { return "****" + card.substring(card.length() - 4); }
public static void main(String[] args) {
log.info("charged card {}", mask("4111111111111234"));
}
}一个贴近实际的服务
综合运用:让一个小型服务在适当的级别记录日志,并处理异常路径。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EmailService {
private static final Logger log = LoggerFactory.getLogger(EmailService.class);
void send(String to) {
log.debug("preparing email to {}", to);
try {
if (to == null) throw new IllegalArgumentException("null recipient");
log.info("email sent to {}", to);
} catch (Exception e) {
log.error("failed to send email", e);
}
}
}快速检查
测试您对 SLF4J 基础知识的掌握程度。
回顾
您已经学习了 SLF4J 门面:
- 每个类获取一个
private static final Logger。 - 恰当地使用五个级别方法。
- 日志记录器名称具有层级结构,可以按软件包进行控制。
- 将
Throwable放在最后传入,以记录堆栈跟踪;绝不要记录机密信息。
接下来将学习高效的参数化消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
log.info("SLF4J facade recap complete");
}
}常见问题解答
「SLF4J 外观与日志记录器」课时是免费的吗?
是的 — 「SLF4J 外观与日志记录器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「SLF4J 外观与日志记录器」这节课中我会学到什么?
在正确的级别记录日志 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「SLF4J 外观与日志记录器」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 结构化日志为何重要
- SLF4J 外观与日志记录器
- 参数化日志记录
- 配置 Logback