Java Academy · 课时

结构化日志为何重要

不止是 System.out.println

第 1 / 4 课13 个步骤

结构化日志为何重要 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

println 的问题

System.out.println 适合快速实验,但对于实际应用来说,它不是好的日志记录工具。

  • 没有时间戳、级别或线程名称。
  • 无法在生产环境中将其关闭。
  • 只能写入标准输出,无法进行路由。
public class Main {
    public static void main(String[] args) {
        // Crude: no level, no timestamp, always on
        System.out.println("User logged in");
    }
}

日志框架带来的功能

日志框架可以提供:

  • 使用 级别(TRACE、DEBUG、INFO、WARN、ERROR)过滤噪声。
  • 自动添加时间戳、线程和日志记录器名称。
  • 将日志路由到文件、控制台或远程系统。
  • 无需重新编译即可进行运行时配置。
public class Main {
    public static void main(String[] args) {
        // A framework would render: time, level, logger, thread, message
        String simulated = "2026-01-01 10:00:00 INFO  c.e.App [main] - User logged in";
        System.out.println(simulated);
    }
}

日志级别说明

级别按照消息的重要性进行排序:

  • TRACE:极其细致的详细信息。
  • DEBUG:开发诊断信息。
  • INFO:正常进度节点。
  • WARN:出现意外情况,但可以恢复。
  • ERROR:需要关注的故障。
public class Main {
    enum Level { TRACE, DEBUG, INFO, WARN, ERROR }
    public static void main(String[] args) {
        Level threshold = Level.INFO;
        for (Level l : Level.values()) {
            boolean shown = l.ordinal() >= threshold.ordinal();
            System.out.println(l + " visible? " + shown);
        }
    }
}

按阈值过滤

日志记录器有一个阈值。低于阈值的消息会以很低的成本被丢弃。

在生产环境中,您可以将阈值设为 INFO,使 DEBUG 和 TRACE 消失,在不修改代码的情况下减少噪声和成本。

public class Main {
    enum Level { TRACE, DEBUG, INFO, WARN, ERROR }
    static void log(Level threshold, Level level, String msg) {
        if (level.ordinal() >= threshold.ordinal()) {
            System.out.println(level + " - " + msg);
        }
    }
    public static void main(String[] args) {
        Level t = Level.WARN;
        log(t, Level.DEBUG, "hidden detail");
        log(t, Level.ERROR, "shown error");
    }
}

结构化与非结构化

非结构化日志是普通句子。结构化日志会附加键值上下文(用户标识符、请求标识符),供机器解析。

结构化日志可以在 Elasticsearch 或 Loki 等工具中搜索和聚合。

import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // Structured context as key-value pairs
        Map<String, Object> fields = Map.of("event", "login", "userId", 42, "ok", true);
        System.out.println(fields);
    }
}

门面思想

您不应将代码绑定到特定的日志库。像 SLF4J 这样的门面让您可以针对一个接口编写代码,并在以后替换后端(Logback、Log4j2)。

这样可以使应用程序与日志实现方案的选择解耦。

public class Main {
    public static void main(String[] args) {
        // Concept: your code calls a facade interface, not a concrete logger
        System.out.println("App -> SLF4J facade -> Logback backend");
    }
}

为什么不直接使用布尔标志

有些代码会使用 if (DEBUG) System.out.println(...)。这会使条件判断散布在整个代码库中,而且无法按包进行控制。

日志框架会集中管理配置:为一个包启用 DEBUG,对其他所有包使用 ERROR。

public class Main {
    static final boolean DEBUG = false;
    public static void main(String[] args) {
        // Inflexible: one global switch, scattered checks
        if (DEBUG) System.out.println("diagnostic");
        System.out.println("A framework controls this per logger instead");
    }
}

性能注意事项

未启用时,日志记录应当具有很低的成本。框架会跳过被过滤级别的工作,而参数化消息可以避免构建随后会被丢弃的字符串。

打印语句总会计算其参数,即使没有人读取输出也是如此。

public class Main {
    static String expensive() {
        System.out.println("(building expensive string)");
        return "big report";
    }
    public static void main(String[] args) {
        boolean debugEnabled = false;
        // Only build the message if it will actually be logged
        if (debugEnabled) System.out.println(expensive());
        System.out.println("skipped expensive work");
    }
}

集中式输出路由

框架会将日志发送到一个或多个附加器:控制台、滚动文件、系统日志或网络收集器。

您只需配置一次路由,应用程序代码无需改变。

public class Main {
    public static void main(String[] args) {
        String[] appenders = {"CONSOLE", "ROLLING_FILE", "JSON_HTTP"};
        for (String a : appenders) System.out.println("route logs to: " + a);
    }
}

关联与上下文

实际系统会在一次请求的每一行日志中加入请求标识符,这样您就可以跨线程和服务跟踪一位用户的整个流程。

SLF4J 通过 MDC(映射诊断上下文)支持这一功能。

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> mdc = new HashMap<>();
        mdc.put("requestId", "req-1234");
        System.out.println("All logs in this request tagged with " + mdc);
    }
}

计划

在本课程中,您将:

  • 使用 SLF4J 门面并获取日志记录器。
  • 在合适的级别记录日志。
  • 使用参数化消息提高效率。
  • 使用附加器和模式配置 Logback。
public class Main {
    public static void main(String[] args) {
        System.out.println("Next: SLF4J facade and loggers");
    }
}

快速检查

测试您对结构化日志记录的理解。

回顾

您已经了解了结构化日志记录的重要性:

  • println 缺少级别、元数据和路由功能。
  • 框架增加了级别、筛选和附加器。
  • 门面让代码与后端解耦。
  • 结构化上下文和 MDC 支持关联不同日志。

接下来将学习 SLF4J 门面以及如何创建日志记录器。

public class Main {
    public static void main(String[] args) {
        System.out.println("Structured logging recap complete");
    }
}
免费开始

用 AI 导师学习 Java — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
104
课程
374

常见问题解答

「结构化日志为何重要」课时是免费的吗?

是的 — 「结构化日志为何重要」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「结构化日志为何重要」这节课中我会学到什么?

不止是 System.out.println 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「结构化日志为何重要」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 结构化日志为何重要
  2. SLF4J 外观与日志记录器
  3. 参数化日志记录
  4. 配置 Logback
← 返回 Java Academy