Spring Boot 3.4 中的結構化日誌

工程 | Moritz Halbritter | 2024 年 8 月 23 日 | ...

日誌是應用程式故障排除中長期存在的一部分,也是可觀測性的三大支柱之一,與指標和追蹤並列。沒有人喜歡在生產環境中盲目執行,當事故發生時,開發者很高興有日誌檔案。日誌通常以人類可讀的格式輸出。

結構化日誌是一種技術,日誌輸出以明確定義、通常是機器可讀的格式寫入。這種格式可以輸入到日誌管理系統,並支援強大的搜尋和分析功能。結構化日誌最常用的格式之一是 JSON。

Spring Boot 3.4 開箱即用地支援結構化日誌。它支援 Elastic Common Schema (ECS)Logstash 格式,但也可以使用您自己的格式進行擴充套件。

讓我們直接開始看看它是如何工作的!

結構化日誌 Hello World

start.spring.io 上建立一個新專案。您無需新增任何依賴項,但請確保至少選擇 Spring Boot 3.4.0-M2。

要在控制檯上啟用結構化日誌記錄,請將以下內容新增到您的 application.properties

logging.structured.format.console=ecs

這將指示 Spring Boot 以 Elastic Common Schema (ECS) 格式記錄日誌。

現在啟動應用程式,您將看到日誌以 JSON 格式格式化

{"@timestamp":"2024-07-30T08:41:10.561295200Z","log.level":"INFO","process.pid":67455,"process.thread.name":"main","service.name":"structured-logging-demo","log.logger":"com.example.structured_logging_demo.StructuredLoggingDemoApplication","message":"Started StructuredLoggingDemoApplication in 0.329 seconds (process running for 0.486)","ecs.version":"8.11"}

很酷,對吧?現在讓我們深入探討更高階的主題。

結構化日誌到檔案

您也可以將結構化日誌記錄到檔案。例如,這可以用於在控制檯上列印人類可讀的日誌,並將結構化日誌寫入檔案以供機器攝取。

為此,請將以下內容新增到您的 application.properties 中,並確保刪除 logging.structured.format.console=ecs 設定

logging.structured.format.file=ecs
logging.file.name=log.json

現在啟動您的應用程式,您將看到控制檯上有可讀的日誌,檔案 log.json 包含機器可讀的 JSON 內容。

新增附加欄位

結構化日誌的一個強大功能是開發者可以以結構化的方式向日志事件新增資訊。例如,您可以將使用者 ID 新增到每個日誌事件中,然後稍後根據該 ID 進行過濾,以檢視特定使用者執行了哪些操作。

Elastic Common Schema 和 Logstash 都包含 Mapped Diagnostic Context 的內容在 JSON 中。為了看看實際效果,讓我們建立自己的日誌訊息

@Component
class MyLogger implements CommandLineRunner {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);

    @Override
    public void run(String... args) {
        MDC.put("userId", "1");
        LOGGER.info("Hello structured logging!");
        MDC.remove("userId");
    }
}

在記錄日誌訊息之前,這段程式碼還在 MDC 中設定了使用者 ID。Spring Boot 會自動將使用者 ID 包含在 JSON 中

{ ... ,"message":"Hello structured logging!","userId":"1" ... }

您還可以使用 流式日誌記錄 API 來新增附加欄位,而無需依賴 MDC

@Component
class MyLogger implements CommandLineRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);

    @Override
    public void run(String... args) {
        LOGGER.atInfo().setMessage("Hello structured logging!").addKeyValue("userId", "1").log();
    }

}

Elastic Common Schema 定義了許多欄位名,Spring Boot 內建支援服務名稱、服務版本、服務環境和節點名稱。要為這些欄位設定值,您可以在 application.properties 中使用以下內容

logging.structured.ecs.service.name=MyService
logging.structured.ecs.service.version=1
logging.structured.ecs.service.environment=Production
logging.structured.ecs.service.node-name=Primary

檢視 JSON 輸出時,現在包含 service.nameservice.versionservice.environmentservice.node.name 等欄位。有了這些,您現在可以在日誌系統中根據節點名稱、服務版本等進行過濾。

自定義日誌格式

如上所述,Spring Boot 開箱即用地支援 Elastic Common Schema 和 Logstash 格式。要新增您自己的格式,您需要執行以下步驟

  1. 建立一個 StructuredLogFormatter 介面的自定義實現
  2. application.properties 中引用您的自定義實現

首先,讓我們建立自己的自定義實現

class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {

    @Override
    public String format(ILoggingEvent event) {
        return "time=" + event.getTimeStamp() + " level=" + event.getLevel() + " message=" + event.getMessage() + "\n";
    }

}

如您所見,結構化日誌記錄支援不限於 JSON,您可以返回任何您想要的 String。在此示例中,我們選擇使用 key=value 對。

現在我們需要讓 Spring Boot 知道我們的自定義實現。為此,請將以下內容新增到 application.properties

logging.structured.format.console=com.example.structured_logging_demo.MyStructuredLoggingFormatter

是時候啟動應用程式並驚歎於日誌輸出了!

time=1722330118045 level=INFO message=Hello structured logging!

哇,看看那個!多麼漂亮的日誌輸出啊!

如果您想寫入 JSON,Spring Boot 3.4 中有一個方便的新 JsonWriter,您可以使用它

class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {

    private final JsonWriter<ILoggingEvent> writer = JsonWriter.<ILoggingEvent>of((members) -> {
        members.add("time", (event) -> event.getInstant());
        members.add("level", (event) -> event.getLevel());
        members.add("thread", (event) -> event.getThreadName());
        members.add("message", (event) -> event.getFormattedMessage());
        members.add("application").usingMembers((application) -> {
            application.add("name", "StructuredLoggingDemo");
            application.add("version", "1.0.0-SNAPSHOT");
        });
        members.add("node").usingMembers((node) -> {
           node.add("hostname", "node-1");
           node.add("ip", "10.0.0.7");
        });
    }).withNewLineAtEnd();

    @Override
    public String format(ILoggingEvent event) {
        return this.writer.writeToString(event);
    }

}

當然,您也可以使用任何其他 JSON 庫(例如 Jackson)來建立 JSON,您不必使用 JsonWriter

生成的日誌訊息看起來像這樣

{"time":"2024-07-30T09:14:49.377308361Z","level":"INFO","thread":"main","message":"Hello structured logging!","application":{"name":"StructuredLoggingDemo","version":"1.0.0-SNAPSHOT"},"node":{"hostname":"node-1","ip":"10.0.0.7"}}

總結

希望您喜歡 Spring Boot 3.4 中的這一新功能!文件也已更新以包含結構化日誌記錄的內容。

請告訴我們您的想法,如果您發現任何問題,我們的問題跟蹤器始終開放!

獲取 Spring 時事通訊

訂閱 Spring 時事通訊,保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助力您的進步。

瞭解更多

獲取支援

Tanzu Spring 透過一個簡單的訂閱提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

即將到來的活動

檢視 Spring 社群所有即將到來的活動。

檢視全部