JavaロギングフレームワークJULの詳細な説明

要約: JavaネイティブロギングフレームワークであるJUL(Java util logging)は、サードパーティの依存関係を導入する必要がなく、シンプルで使いやすいです。

この記事は、HUAWEICLOUDコミュニティ「 JavaLogFramework JUL詳細説明Daquan」、作成者:ChenPiのJavaLibから共有されています。

JULの紹介

JavaネイティブロギングフレームワークであるJUL(Java util logging)は、サードパーティの依存関係を導入する必要がなく、シンプルで使いやすいです。一般的に小さなアプリケーションで使用され、主流のプロジェクトではほとんど使用されません。

7月のアーキテクチャ

  • アプリケーション:Javaアプリケーション。
  • ロガー:ロガー、Javaアプリケーションは、ロガーのメソッドを呼び出すことによってログレコードを公開します。
  • ハンドラー:ハンドラー、各ロガーは1つ以上のハンドラーに関連付けることができます。ハンドラーはロガーからログを取得して特定の宛先にログを出力します。宛先はコンソール、ローカルファイル、またはネットワークログサービスにするか、OSログに転送します。等々。ハンドラーは、Handler.setLevel(level.off)メソッドを使用して無効にするか、他のレベルを設定してこのハンドラーを有効にすることができます。
  • フィルタ:フィルタ。条件に基づいてフィルタリングするレコードをログに記録します。各ロガーとハンドラーは、フィルターに関連付けることができます。
  • フォーマッター:ログイベントのログレコードの変換とフォーマットを担当するフォーマッター。
  • レベル:各ログレコードには、ログの重要性と緊急性を示すログレベルが関連付けられています。ロガーとハンドラーに関連付けられたログレベルを設定することもできます。

はじめに例

package com.chenpi;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class ChenPiJULMain {

  // JUL 日志框架演示
  @Test
  public void testLog() {

    // 获取日志记录器对象
    Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");

    // 日志记录输出
    logger.severe(">>>>> Hello ChenPi!!");
    logger.warning(">>>>> Hello ChenPi!!");
    logger.info(">>>>> Hello ChenPi!!"); // 默认日志级别
    logger.config(">>>>> Hello ChenPi!!");
    logger.fine(">>>>> Hello ChenPi!!");
    logger.finer(">>>>> Hello ChenPi!!");
    logger.finest(">>>>> Hello ChenPi!!");

    // 输出指定日志级别的日志记录
    logger.log(Level.WARNING, ">>>>> Hello Warning ChenPi!!");

    // 占位符形式
    String name = "ChenPi";
    int age = 18;
    logger.log(Level.INFO, ">>>>> Hello {0},{1} years old!", new Object[]{name, age});

    // 异常堆栈信息
    logger.log(Level.SEVERE, ">>>>> Hello NPE!", new NullPointerException());

  }
}

JULはデフォルトでログ情報をコンソールに出力し、デフォルトのログレベルはinfoです。コンソール出力は次のとおりです。

ロガーの親子継承関係

JULでは、ロガーは親子継承関係の概念を持っており、親子継承関係はロガーオブジェクトの名前の包含関係に従って分割されます。Loggerオブジェクトの場合、明示的に作成された親Loggerオブジェクトが見つからない場合、その親LoggerはルートLogger、つまりRootLoggerです。

子ロガーオブジェクトは、ログレベル、関連するハンドラー、ログ形式など、親ロガーの構成を継承します。Loggerオブジェクトの場合、特別に構成されていない場合、最終的にはRootLoggerの構成を継承します。

package com.chenpi;

import java.util.logging.Logger;
import org.junit.Test;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class ChenPiJULMain {

  @Test
  public void testLog() {

    // logger1的父Logger是RootLogger
    Logger logger1 = Logger.getLogger("com.chenpi.a");
    // logger2的父Logger是logger1  
    Logger logger2 = Logger.getLogger("com.chenpi.a.b.c");

    Logger logger1Parent = logger1.getParent();
    System.out.println("logger1Parent:" + logger1Parent + ",name:" + logger1Parent.getName());

    Logger logger2Parent = logger2.getParent();
    System.out.println("logger1:" + logger1 + ",name:" + logger1.getName());
    System.out.println("logger2Parent:" + logger2Parent + ",name:" + logger2Parent.getName());

  }
}

// 输出结果如下
logger1Parent:java.util.logging.LogManager$RootLogger@61e717c2,name:
logger1:java.util.logging.Logger@66cd51c3,name:com.chenpi.a
logger2Parent:java.util.logging.Logger@66cd51c3,name:com.chenpi.a

ログ構成

JULのデフォルトのログ動作(ログレベルの設定、ログ出力先、ログ形式など)は、2つの方法で調整できます。1つはプログラムのフォームをハードコーディングする方法(非推奨)、もう1つは個別の構成ファイル形式です。

ハードコードされたロギング構成

JULでは、ロガーには親子の継承関係があるため、ロガーオブジェクトを個別に構成する必要がある場合は、親ロガーの構成を継承しないように設定する必要があります。

以下は、com.chenpi.ChenPiJULMainという名前のLoggerオブジェクトが個別に構成され、2つのハンドラーに関連付けられていることを示しています。

package com.chenpi;

import java.io.IOException;
import java.util.logging.*;
import org.junit.Test;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class ChenPiJULMain {

  @Test
  public void testLog() throws IOException {

    // 获取日志记录器对象
    Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");

    // 关闭默认配置,即不使用父Logger的Handlers
    logger.setUseParentHandlers(false);

    // 设置记录器的日志级别为ALL
    logger.setLevel(Level.ALL);

    // 日志记录格式,使用简单格式转换对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();

    // 控制台输出Handler,并且设置日志级别为INFO,日志记录格式
    ConsoleHandler consoleHandler = new ConsoleHandler();
    consoleHandler.setLevel(Level.INFO);
    consoleHandler.setFormatter(simpleFormatter);

    // 文件输出Handler,并且设置日志级别为FINE,日志记录格式
    FileHandler fileHandler = new FileHandler("./jul.log");
    fileHandler.setLevel(Level.FINE);
    fileHandler.setFormatter(simpleFormatter);

    // 记录器关联处理器,即此logger对象的日志信息输出到这两个Handler进行处理
    logger.addHandler(consoleHandler);
    logger.addHandler(fileHandler);

    // 日志记录输出
    logger.severe(">>>>> Hello ChenPi!!");
    logger.warning(">>>>> Hello ChenPi!!");
    logger.info(">>>>> Hello ChenPi!!");
    logger.config(">>>>> Hello ChenPi!!");
    logger.fine(">>>>> Hello ChenPi!!");
    logger.finer(">>>>> Hello ChenPi!!");
    logger.finest(">>>>> Hello ChenPi!!");
  }
}

ロガーによって設定されたログレベルはALLであるため、つまり、すべてのレベルのログレコードが合格できます。ただし、ConsoleHandlerによって設定されるログレベルはINFOであるため、コンソールはINFOレベルを超えるログレコードのみを出力します。FileHandlerによって設定されるログレベルはFINEであるため、FINEレベルを超えるログレコードがログファイルに出力されます。

コンソールからのログ出力は次のとおりです。

以下は、ログファイルjul.logに出力されるログ結果です。

ログ設定ファイル

デバッグデバッグにより、次のメソッドシーケンスに従って、JUL構成ファイルを構成しない場合、システムはデフォルトでJDKインストールディレクトリの下のlibディレクトリからデフォルト構成ファイルlogging.propertiesを読み取ることがわかります。

getLogger()  -> demandLogger() -> LogManager.getLogManager() -> ensureLogManagerInitialized() -> owner.readPrimordialConfiguration() -> readConfiguration()

以下は、JDKに付属するJULデフォルトのログ構成ファイルの内容です。

設定オプションについては、対応するクラスのソースコードから実際に確認できます。たとえば、Loggerクラスのソースコードは次のとおりです。

FileHandlerクラスのソースコードは次のとおりです。

したがって、デフォルトの構成ファイルをプロジェクトのリソースディレクトリにコピーして、構成情報をカスタマイズできます。

############################################################
# 全局属性
############################################################

# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL
############################################################
# Handler 配置
############################################################

# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 50000
# 文件数量上限,多个文件为jul0.log.0,jul0.log.1 ...
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = SEVERE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Logger 配置
############################################################

# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING

次に、アプリケーションのクラスパスにカスタム構成ファイルをロードします。

package com.chenpi;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class ChenPiJULMain {

  // JUL 日志框架演示
  @Test
  public void testLog() throws IOException {

    // 读取配置文件
    // 也可以通过使用java.util.logging.config.file系统属性指定文件名
    // 例如 java -Djava.util.logging.config.file=myfile
    InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
        .getResourceAsStream("logging.properties");
    // 获取LogManager
    LogManager logManager = LogManager.getLogManager();
    // 记载配置文件
    logManager.readConfiguration(resourceAsStream);

    // 获取日志记录器对象
    Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");

    // 日志记录输出
    logger.severe(">>>>> Hello ChenPi!!");
    logger.warning(">>>>> Hello ChenPi!!");
    logger.info(">>>>> Hello ChenPi!!");
    logger.config(">>>>> Hello ChenPi!!");
    logger.fine(">>>>> Hello ChenPi!!");
    logger.finer(">>>>> Hello ChenPi!!");
    logger.finest(">>>>> Hello ChenPi!!");

    // 获取日志记录器对象
    Logger personLogger = Logger.getLogger("com.chenpi.person");

    // 日志记录输出
    personLogger.severe(">>>>> Hello Person!!");
    personLogger.warning(">>>>> Hello Person!!");
    personLogger.info(">>>>> Hello Person!!");
    personLogger.config(">>>>> Hello Person!!");
    personLogger.fine(">>>>> Hello Person!!");
    personLogger.finer(">>>>> Hello Person!!");
    personLogger.finest(">>>>> Hello Person!!");
  }
}

コンソールとファイルの出力は次のとおりです。

カスタムロガー

デフォルトで親から継承する代わりに、ログレベル、関連するハンドラーなど、ロガーを個別に構成できます。

もちろん、パッケージ名を使用してロガーを構成することもできるため、このパッケージ名のすべての子ロガーはこの構成を継承できます。

############################################################
# 全局属性
############################################################

# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL
############################################################
# Handler 配置
############################################################

# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 50000
# 文件数量上限
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = SEVERE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Logger 配置
############################################################

# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING
# 只关联FileHandler
com.chenpi.person.handlers = java.util.logging.FileHandler
# 关闭默认配置
com.chenpi.person.useParentHandlers = false
package com.chenpi;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;

/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/2
*/
public class ChenPiJULMain {
 
    // JUL 日志框架演示
    @Test
    public void testLog() throws IOException {
 
        // 读取配置文件
        InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
            .getResourceAsStream("logging.properties");
        // 获取LogManager
        LogManager logManager = LogManager.getLogManager();
        // 记载配置文件
        logManager.readConfiguration(resourceAsStream);
 
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
 
        // 日志记录输出
        logger.severe(">>>>> Hello ChenPi!!");
        logger.warning(">>>>> Hello ChenPi!!");
        logger.info(">>>>> Hello ChenPi!!");
        logger.config(">>>>> Hello ChenPi!!");
        logger.fine(">>>>> Hello ChenPi!!");
        logger.finer(">>>>> Hello ChenPi!!");
        logger.finest(">>>>> Hello ChenPi!!");
 
        // 获取日志记录器对象
        Logger personLogger = Logger.getLogger("com.chenpi.person");
 
        // 日志记录输出
        personLogger.severe(">>>>> Hello Person!!");
        personLogger.warning(">>>>> Hello Person!!");
        personLogger.info(">>>>> Hello Person!!");
        personLogger.config(">>>>> Hello Person!!");
        personLogger.fine(">>>>> Hello Person!!");
        personLogger.finer(">>>>> Hello Person!!");
        personLogger.finest(">>>>> Hello Person!!");
    }
}

上記の例では、com.chenpi.personという名前のロガーの場合、ログのみがファイルに出力され、他のロガーはコンソールとファイルに同時に出力されます。

カスタムログ形式

SimpleFormatterクラスのソースコードを例として取り上げ、LoggingSupportクラスのソースコードに移動すると、最初にjava.util.logging.SimpleFormatter.formatプロパティを使用してフォーマットを構成したかどうかを判断し、構成していない場合は、デフォルトのログ形式。

したがって、構成ファイルへのロギングの形式をカスタマイズできます。

############################################################
# 全局属性
############################################################

# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL
############################################################
# Handler 配置
############################################################

# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 1024
# 文件数量上限
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = FINE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 自定义SimpleFormatter的日志格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Logger 配置
############################################################

# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING

このようにして、SimpleFormatterにバインドされたすべてのハンドラーのすべてのログレコードがカスタム形式を使用します。

もちろん、ソースコードを通じて、次のように、ログ形式クラスの他のいくつかの実装があります。

ログフィルター

フィルタ、ログフィルタ、出力ログレコードのフィルタリングに使用されます。メッセージに特定のテキスト情報が含まれているログのみを出力する、特定の方法で記録されたログのみを出力する、特定のレベルのログのみを出力するなど、複数の次元でフィルタリングできます。

Loggerオブジェクトは、ログ情報をLogRecordオブジェクトにラップしてから、オブジェクトをハンドラーに渡して処理します。各ロガーとハンドラーは、フィルターに関連付けることができます。LogRecordには、ログのテキスト情報、ログ生成のタイムスタンプ、ログの取得元のクラス、ログの取得元のメソッド、ログの取得元のスレッドなどが含まれます。

Filterのソースコードを以下に示します。Filterの実装クラスを作成してメソッドを書き直すだけです。

package java.util.logging;

/**
* A Filter can be used to provide fine grain control over
* what is logged, beyond the control provided by log levels.
* <p>
* Each Logger and each Handler can have a filter associated with it.
* The Logger or Handler will call the isLoggable method to check
* if a given LogRecord should be published.  If isLoggable returns
* false, the LogRecord will be discarded.
*
* @since 1.4
*/
@FunctionalInterface
public interface Filter {
 
    /**
    * Check if a given log record should be published.
    * @param record  a LogRecord
    * @return true if the log record should be published.
    */
    public boolean isLoggable(LogRecord record);
}

フィルタを実装します。ログメッセージに暴力という単語が含まれている場合、それは解放されません。つまり、このログは記録されません。

package com.chenpi;

import java.util.logging.Filter;
import java.util.logging.LogRecord;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class MyLoggerFilter implements Filter {

  private static final String SENSITIVE_MESSAGE = "暴力";

  @Override
  public boolean isLoggable(LogRecord record) {
    String message = record.getMessage();
    return null == message || !message.contains(SENSITIVE_MESSAGE);
  }
}

次に、このフィルターオブジェクト設定をロガーにバインドできます。

package com.chenpi;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;

/**
 * @author 陈皮
 * @version 1.0
 * @description
 * @date 2022/3/2
 */
public class ChenPiJULMain {

  // JUL 日志框架演示
  @Test
  public void testLog() throws IOException {

    // 读取配置文件
    InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader()
        .getResourceAsStream("logging.properties");
    // 获取LogManager
    LogManager logManager = LogManager.getLogManager();
    // 记载配置文件
    logManager.readConfiguration(resourceAsStream);

    // 获取日志记录器对象
    Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");
    // Logger关联过滤器
    logger.setFilter(new MyLoggerFilter());

    // 日志记录输出
    logger.info(">>>>> Hello ChenPi!!");
    logger.info(">>>>> 暴力小孩!!");
  }
}

// 输出结果如下
信息: >>>>> Hello ChenPi!! [星期三 三月 02 15:31:06 CST 2022]

 

[フォロー]をクリックして、HUAWEI CLOUDの新技術について初めて学びましょう〜

{{o.name}}
{{m.name}}

おすすめ

転載: my.oschina.net/u/4526289/blog/5519619