ログ フレームワークの概要 - Slf4j+Logback エントリの実践 | JD Cloud テクニカル チーム

序文

インターネットとビッグデータの急速な発展に伴い、分散ログ システムとログ分析システムが広く使用されており、ほぼすべてのアプリケーションがさまざまなログ フレームワークを使用してプログラムの実行情報を記録しています。したがって、エンジニアとして、主流のロギング フレームワークを理解することが非常に重要です。アプリケーションの実行結果はログの有無に影響されませんが、ログのないアプリケーションは不完全であるか、欠陥があることさえあります。優れたログ システムは、操作の追跡を記録しシステムの動作状態を監視しシステムの障害を解決できます




Java ロギング フレームワークの進化

初期の頃、Java ロギング フレームワークは統一された標準を策定していなかったので、多くのアプリケーションが複数のロギング フレームワークを同時に使用していました。Java ロギング フレームワークの開発の歴史は、次の段階に大別できます。





 

1. Log4j: Apache Log4j は Java ベースのロギング ツールです。このプロジェクトは 1999 年に Ceki Gülcü によって作成され、Java ロギング フレームワークの事実上の標準となっています。
2. 7 月: Apache は Log4j を jdk に導入したいと考えましたが、Sun によって拒否されました。その後、Sun は Log4j を模倣し、jdk1.4 で JUL (java.util.logging) を導入しました。
3. コモンズ ロギング:ロギング インターフェイスと実装を分離するために、Apache は 2002 年に JCL (Jakarta Commons Logging) を開始しました。JCL はログ インターフェイスのセットを定義し、特定の実装は Log4j または JUL によって完了します。Commons Logging は動的バインディングを使用してロギングを実装します。コーディングするときは、定義されているインターフェイスを使用するだけで済みます。プログラムの実行中は、ClassLoader を使用して基礎となるログ ライブラリを検索してロードします。そのため、Log4j または JUL を柔軟に選択して、ロギング機能を実装します。
4. Slf4j&Logback: Ceki Gülcü と Apache Foundation は Commons-Logging 標準について意見が異なります。その後、Ceki Gülcü は Apache を離れ、Slf4j と Logback という 2 つのプロジェクトを作成しました。Slf4j はインターフェイスのみを提供するログ ファサードであり、Logback、JUL、log4j などのログ実装をサポートできます。Logback は特定の実装を提供します。log4j と比較して、Logback は実行速度が速く、機能がより充実しています。
5. Log4j 2: Java ログ分野での地位を維持し、JCL と Log4j が Slf4j と Logback に置き換えられるのを防ぐために、Apache は 2014 年に Log4j 2 を開始しました。Log4j 2 は log4j と互換性がありませんが、綿密な最適化を行った結果、パフォーマンスが大幅に向上しました。



ログフレームワークの概要

前述したように、現在一般的に使用されているロギング フレームワークには、Log4j、Log4j 2、Commons Logging、Slf4j、Logback、JUL などがあります。これらのロギング フレームワークは、ファサード ロギングとロギング システムの 2 つのタイプに分類できます。

丸太ファサード

ロギング ファサードは、アプリケーションにロギングするための抽象化レイヤーを実装するために使用されるデザイン パターンです。特定のインターフェイス実装を提供せずに、インターフェイスとメソッドの統一されたセット、つまり対応する API を提供します。ログ ファサードを使用すると、特定のログ フレームワークの実装を動的または静的に指定できるため、インターフェイスと実装が分離され、ユーザーが特定のログ フレームワークを柔軟に選択できるようになります。

ロギングシステム

ログ システムとは、アプリケーションの実行時に生成されるログ情報を記録および管理するために使用されるソフトウェア ツールまたはフレームワークを指します。ログ ファサードと比較して、アプリケーションはログ レベル管理、ログのフォーマット、ログ出力ターゲットの設定などのログ出力機能を実行する、特定のログ インターフェイスの実装を提供します。一般的なログ システムには、Log4j、Logback、Java Util Logging などが含まれます。





 

ログ ファサードを使用すると、アプリケーションへのログインに統合 API を使用でき、必要に応じて特定のログ実装を選択して構成できます。このようにして、アプリケーション コードに大きな影響を与えることなく、プロジェクトのニーズやチームの好みに応じてロギング システムを柔軟に選択、切り替え、構成できます。

循環依存関係を避ける

Slf4j の作者である Ceki Gülcü 氏は、Commons-Logging の API が適切に設計されておらず、パフォーマンスが十分に高くないと感じたため、Slf4j を設計しました。Slf4j がさまざまなタイプのログ システム実装と互換性を持つようにするために、次の図に示すように、接続するためのかなりの数のアダプターとブリッジも設計しました。





 

このことを考慮して、ログ フレームワークの依存関係を導入しないようにしてください。たとえば、次の組み合わせを同時に使用することはできません。

jcl-over-slf4j および slf4j-jcl
log4j-over-slf4j および slf4j-log4j12
jul-to-slf4j 和 slf4j-jdk14

ログフレームワークの使用オプション

一般的に使用される組み合わせは、Slf4j & LogbackおよびCommons Logging & Log4jです。

推薦する

Slf4j とログバック

理由

1. Slf4j の実装メカニズムにより、Slf4j には制限が少なく、使用範囲が広くなります。Commons-Logging と比較すると、Slf4j はコンパイル中にローカル ログ ライブラリを静的にバインドし、その汎用性がはるかに優れています。

2. ログバックのパフォーマンスが向上します。Logback は、ログ ステートメントを記録するかどうかの決定など、特定の主要な操作のパフォーマンスが大幅に向上したと主張しており、この操作にかかる時間は Logback ではわずか 3 ns ですが、Log4j では 30 ns です。

3.次の例に示すように、Slf4j はパラメータ化をサポートし、プレースホルダー シンボルを使用してコードをより簡潔にします。

// 在使用 Commons-Logging 时,通常的做法是 
if(log.isDebugEnabled()){ 
    log.debug("User name: " + user.getName() + " buy goods id :" + good.getId()); 
} 

// 在 Slf4j 阵营,你只需这么做: 
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());

4. Logback のすべてのドキュメントは無料で提供されますが、Log4j は一部の無料ドキュメントのみを提供し、ユーザーは有料ドキュメントを購入する必要があります。

5. MDC (マップされた診断コンテキスト) フィルターを使用して、現在のユーザー名などのビジネス情報を MDC に入力します。この変数はログ形式の定義で使用できます。具体的には、問題を診断するときに、ログが必要になることがよくあります。Log4j を使用する場合は、ログ レベルを下げることしかできませんが、これにより大量のログが生成され、アプリケーションのパフォーマンスに影響します。Logback を使用する場合は、元のログ レベルを維持し、特定の特殊な状況を除外してください。アリスがログインすると、ログは DEBUG レベルで記録されますが、他のユーザーは WARN レベルでプレイを続けることができます。この機能を実装するには、4 行の XML 設定を追加するだけです。

6. ログを自動的に圧縮します。RollingFileAppender は、新しいファイルを生成するときに、ログに記録されたファイルを自動的に圧縮します。圧縮プロセスは非同期であるため、アプリケーションは圧縮プロセス中に実質的に影響を受けません。




Slf4j+Logback 入門の練習

Mavenの依存関係

pom.xml

<!--日志框架接口-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<!--日志框架接口实现-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>
<!--日志框架核心组件-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
</dependency>

<!--自动化注解工具-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>

設定ファイル

ログバック.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!--默认日志配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 控制台日志 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="UTF-8">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Info日志 -->
    <appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-info.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>NEUTRAL</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 -->
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 -->
            <totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 -->
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Warn日志 -->
    <appender name="FILE-WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-warn.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 -->
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 -->
            <totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 -->
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- Error日志 -->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-error.log</file>
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>200MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="info-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-INFO"/>
        <queueSize>512</queueSize> <!-- 异步队列的大小 -->
    </appender>
    <appender name="warn-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-WARN"/>
        <queueSize>512</queueSize> <!-- 异步队列的大小 -->
    </appender>
    <appender name="error-asyn" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE-ERROR"/>
        <queueSize>512</queueSize>
    </appender>

    <!-- 应用日志 -->
    <logger name="com.improve.fuqige.bronze" additivity="false">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE-INFO"/>
        <appender-ref ref="FILE-WARN"/>
        <appender-ref ref="FILE-ERROR"/>
    </logger>


    <!-- 总日志出口 -->
    <root level="${logging.level.root}">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="info-asyn"/>
        <appender-ref ref="warn-asyn"/>
        <appender-ref ref="error-asyn"/>
    </root>
</configuration>

アプリケーションのプロパティ

logging.file=fuqige-bronze
logging.path=XXXXXX/Logs/XXXXXX
logging.level.root=info
logging.level.com.improve.fuqige.bronze=info
logging.pattern.console=%cyan(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow([%thread]) %highlight(%-5level) %boldGreen(%logger{80}[LineNumber:%L]): %highlight(%msg%n)
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] %-5level --- [%thread] %logger{80}[LineNumber:%L]: %msg%n

テストケース

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        log.info("进来了!");
        log.warn("进来了!");
        log.error("进来了!");
        return "hello, world! requestId=" + MDC.get("requestId");
    }
}






参考文献

Java ロギング フレームワーク: https://zhuanlan.zhihu.com/p/365154773

SLF4J フレームワークの一般的な使用法とベスト プラクティス: https://juejin.cn/post/7215569601161166906

著者: JD Retail 張宏

出典:JD Cloud Developer Community 転載の際は出典を明記してください

Broadcom が既存の VMware パートナー プログラム Deepin-IDE バージョン アップデートの終了を発表 、古い外観を新しい外観に置き換える 周 紅逸: 紅蒙ネイティブは間違いなく成功する WAVE SUMMIT は 10 回目のセッションを迎え、温信宜燕氏が最新情報を公開します! ヤクルト社、95Gデータ流出を確認 2023年プログラミング言語で最も人気のライセンス 「2023年中国オープンソース開発者報告書」正式リリース Julia 1.10正式リリース Fedora 40は/usr/binと/usr/sbinを統合予定 Rust 1.75 .0リリース
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10443370