java.util.logging (JUL)

Java標準ライブラリに含まれる組み込みロギングAPI。外部依存関係なしで利用可能で、基本的なロギング機能を提供。ログレベル、ハンドラー、フォーマッターなどの標準的な機能を搭載。小規模アプリケーションに適している。

ロギングJava標準ライブラリ組み込み軽量

ライブラリ

Java Util Logging (JUL)

概要

Java Util Logging (JUL)は、Java 1.4から標準ライブラリに組み込まれている公式のロギングフレームワークです。java.util.loggingパッケージで提供され、外部依存なしでログ機能を利用できる軽量なソリューションです。Logger、Handler、Formatterの階層構造により柔軟なログ制御を提供し、設定ファイルやプログラマティックな設定の両方に対応しています。シンプルなAPIながら、エンタープライズアプリケーションでの基本的なログ要件を満たす機能を備えています。

詳細

JULは2002年のJava 1.4リリース以来、Javaプラットフォームの標準ログソリューションとして提供されており、2025年現在でも多くのJavaアプリケーションで使用されています。Logger階層によるログレベル制御(SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST)、多様なHandlerによる出力先制御(ファイル、コンソール、メモリ、ソケット等)、カスタムFormatterによるログフォーマット制御、設定ファイル(logging.properties)による一元管理など、基本的ながら包括的なログ機能を提供します。Java標準ライブラリの一部として高い互換性と安定性を持ちます。

主な特徴

  • 標準ライブラリ組み込み: 外部依存なしでJavaプラットフォームに標準搭載
  • 階層的Logger構造: パッケージ階層に対応したLogger階層管理
  • 7段階ログレベル: SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST
  • 多様なHandler: Console、File、Memory、Socket、Stream等の出力先対応
  • 設定ファイル管理: logging.propertiesによる一元的な設定管理
  • 国際化対応: ResourceBundleを使用したメッセージの国際化機能

メリット・デメリット

メリット

  • Java標準ライブラリのため外部依存なしで導入が簡単
  • JavaプラットフォームとのシームレスなJVM統合と高い互換性
  • 設定ファイルによる実行時のログレベル変更が可能
  • Logger階層により細かいログ制御が可能
  • Handlerの組み合わせによる柔軟な出力先制御
  • 軽量で基本的なログ要件には十分な機能を提供

デメリット

  • Log4j2やLogback等の専門ライブラリと比較して機能が限定的
  • 設定の複雑さや分かりにくさが初心者には障壁となる場合がある
  • パフォーマンスが他の現代的なログライブラリより劣る
  • ログローテーション機能が基本的で高度な制御は困難
  • コミュニティサポートや更新頻度が他のライブラリより少ない
  • 大規模システムでは機能不足により運用が困難な場合がある

参考ページ

書き方の例

基本セットアップ

import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.SimpleFormatter;

public class BasicLoggingSetup {
    // クラス名ベースのLoggerを取得
    private static final Logger logger = Logger.getLogger(BasicLoggingSetup.class.getName());
    
    public static void main(String[] args) {
        try {
            // コンソールハンドラーの設定
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.ALL);
            consoleHandler.setFormatter(new SimpleFormatter());
            
            // ファイルハンドラーの設定
            FileHandler fileHandler = new FileHandler("application.log", true);
            fileHandler.setLevel(Level.INFO);
            fileHandler.setFormatter(new SimpleFormatter());
            
            // Loggerにハンドラーを追加
            logger.addHandler(consoleHandler);
            logger.addHandler(fileHandler);
            logger.setLevel(Level.ALL);
            
            // 親Loggerへの伝播を無効化(重複出力を防ぐ)
            logger.setUseParentHandlers(false);
            
            logger.info("Java Util Loggingが正常に初期化されました");
            
        } catch (Exception e) {
            System.err.println("ログ設定エラー: " + e.getMessage());
        }
    }
}

基本的なログ出力

import java.util.logging.Logger;
import java.util.logging.Level;

public class BasicLogging {
    private static final Logger logger = Logger.getLogger(BasicLogging.class.getName());
    
    public void demonstrateBasicLogging() {
        // 7つのログレベルを使用した出力
        logger.severe("SEVERE: 致命的なエラーが発生しました");
        logger.warning("WARNING: 警告メッセージです");
        logger.info("INFO: 一般的な情報メッセージです");
        logger.config("CONFIG: 設定情報メッセージです");
        logger.fine("FINE: 詳細なデバッグ情報です");
        logger.finer("FINER: より詳細なデバッグ情報です");
        logger.finest("FINEST: 最も詳細なデバッグ情報です");
        
        // Level.OFFは出力されない
        logger.log(Level.OFF, "このメッセージは出力されません");
        
        // パラメータ付きログ
        String userName = "田中太郎";
        int userId = 12345;
        logger.log(Level.INFO, "ユーザー {0} (ID: {1}) がログインしました", 
                  new Object[]{userName, userId});
        
        // 例外情報を含むログ
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            logger.log(Level.SEVERE, "数値計算エラーが発生しました", e);
        }
        
        // 条件付きログ(パフォーマンス最適化)
        if (logger.isLoggable(Level.FINE)) {
            String expensiveDebugInfo = calculateExpensiveDebugInfo();
            logger.fine("詳細デバッグ情報: " + expensiveDebugInfo);
        }
    }
    
    private String calculateExpensiveDebugInfo() {
        // 高コストな処理のシミュレーション
        return "詳細なデバッグデータ";
    }
}

高度な設定(カスタムFormatterとHandler)

import java.util.logging.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

// カスタムフォーマッターの実装
class CustomFormatter extends Formatter {
    private static final DateTimeFormatter DATE_FORMAT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    
    @Override
    public String format(LogRecord record) {
        StringBuilder sb = new StringBuilder();
        
        // タイムスタンプ
        LocalDateTime dateTime = LocalDateTime.ofInstant(
            record.getInstant(), java.time.ZoneId.systemDefault());
        sb.append(dateTime.format(DATE_FORMAT));
        
        // ログレベル
        sb.append(" [").append(record.getLevel()).append("]");
        
        // スレッド情報
        sb.append(" [Thread-").append(record.getThreadID()).append("]");
        
        // クラス名とメソッド名
        if (record.getSourceClassName() != null) {
            sb.append(" ").append(record.getSourceClassName());
            if (record.getSourceMethodName() != null) {
                sb.append(".").append(record.getSourceMethodName());
            }
        }
        
        // メッセージ
        sb.append(" - ").append(formatMessage(record));
        
        // 例外情報
        if (record.getThrown() != null) {
            sb.append("\n");
            java.io.StringWriter sw = new java.io.StringWriter();
            java.io.PrintWriter pw = new java.io.PrintWriter(sw);
            record.getThrown().printStackTrace(pw);
            sb.append(sw.toString());
        }
        
        sb.append("\n");
        return sb.toString();
    }
}

// カスタムフィルターの実装
class CustomFilter implements Filter {
    @Override
    public boolean isLoggable(LogRecord record) {
        // 特定のクラスからのログのみを許可
        String sourceClass = record.getSourceClassName();
        if (sourceClass != null && sourceClass.contains("Security")) {
            return record.getLevel().intValue() >= Level.WARNING.intValue();
        }
        return true;
    }
}

// ローテーション機能付きファイルハンドラー
class RotatingFileHandler extends FileHandler {
    public RotatingFileHandler(String pattern, int limit, int count) 
            throws IOException, SecurityException {
        super(pattern, limit, count, true);
    }
}

public class AdvancedLoggingConfiguration {
    private static final Logger logger = Logger.getLogger(
        AdvancedLoggingConfiguration.class.getName());
    
    public static void setupAdvancedLogging() {
        try {
            // ルートロガーの設定をクリア
            Logger rootLogger = Logger.getLogger("");
            Handler[] handlers = rootLogger.getHandlers();
            for (Handler handler : handlers) {
                rootLogger.removeHandler(handler);
            }
            
            // カスタムコンソールハンドラー
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.WARNING);
            consoleHandler.setFormatter(new CustomFormatter());
            consoleHandler.setFilter(new CustomFilter());
            
            // ローテーション機能付きファイルハンドラー
            // 10MB制限、5ファイルまでローテーション
            RotatingFileHandler fileHandler = new RotatingFileHandler(
                "logs/app-%g.log", 10 * 1024 * 1024, 5);
            fileHandler.setLevel(Level.ALL);
            fileHandler.setFormatter(new CustomFormatter());
            
            // エラー専用ファイルハンドラー
            FileHandler errorHandler = new FileHandler("logs/error.log", true);
            errorHandler.setLevel(Level.WARNING);
            errorHandler.setFormatter(new CustomFormatter());
            errorHandler.setFilter(record -> 
                record.getLevel().intValue() >= Level.WARNING.intValue());
            
            // Loggerに各ハンドラーを追加
            logger.addHandler(consoleHandler);
            logger.addHandler(fileHandler);
            logger.addHandler(errorHandler);
            logger.setLevel(Level.ALL);
            logger.setUseParentHandlers(false);
            
            logger.info("高度なログ設定が完了しました");
            
        } catch (IOException e) {
            System.err.println("ログ設定エラー: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        setupAdvancedLogging();
        
        // テストログ出力
        logger.finest("最詳細ログ");
        logger.fine("詳細ログ");
        logger.info("情報ログ");
        logger.warning("警告ログ");
        logger.severe("重大エラーログ");
    }
}

エラーハンドリングと設定ファイル管理

import java.util.logging.*;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Properties;

public class ConfigurationBasedLogging {
    private static final Logger logger = Logger.getLogger(
        ConfigurationBasedLogging.class.getName());
    
    // 設定ファイルからのログ設定読み込み
    public static void loadLoggingConfiguration() {
        try {
            // logging.propertiesファイルの読み込み
            LogManager.getLogManager().readConfiguration(
                new FileInputStream("config/logging.properties"));
            
            logger.info("ログ設定ファイルを正常に読み込みました");
            
        } catch (IOException e) {
            System.err.println("ログ設定ファイルの読み込みに失敗: " + e.getMessage());
            
            // フォールバック設定
            setupFallbackLogging();
        } catch (SecurityException e) {
            System.err.println("ログ設定のセキュリティエラー: " + e.getMessage());
        }
    }
    
    // フォールバック設定
    private static void setupFallbackLogging() {
        try {
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.INFO);
            consoleHandler.setFormatter(new SimpleFormatter());
            
            logger.addHandler(consoleHandler);
            logger.setLevel(Level.INFO);
            logger.setUseParentHandlers(false);
            
            logger.warning("フォールバックログ設定を適用しました");
            
        } catch (Exception e) {
            System.err.println("フォールバック設定エラー: " + e.getMessage());
        }
    }
    
    // アプリケーション固有のログメソッド
    public static class ApplicationLogger {
        private final Logger appLogger;
        
        public ApplicationLogger(String name) {
            this.appLogger = Logger.getLogger(name);
        }
        
        public void logUserAction(String userId, String action) {
            appLogger.info(String.format("USER_ACTION: %s performed %s", userId, action));
        }
        
        public void logSystemEvent(String event, Object... params) {
            appLogger.log(Level.INFO, "SYSTEM_EVENT: " + event, params);
        }
        
        public void logError(String component, String errorMessage, Throwable throwable) {
            appLogger.log(Level.SEVERE, 
                String.format("ERROR in %s: %s", component, errorMessage), throwable);
        }
        
        public void logPerformanceMetric(String metricName, double value) {
            if (appLogger.isLoggable(Level.CONFIG)) {
                appLogger.config(String.format("METRIC: %s = %.3f", metricName, value));
            }
        }
        
        public void logSecurityEvent(String event, String details) {
            Logger securityLogger = Logger.getLogger("SECURITY." + appLogger.getName());
            securityLogger.warning(String.format("SECURITY_EVENT: %s - %s", event, details));
        }
    }
    
    public static void main(String[] args) {
        // 設定ファイルからの読み込み
        loadLoggingConfiguration();
        
        // アプリケーションロガーの使用例
        ApplicationLogger appLogger = new ApplicationLogger("MyApplication");
        
        try {
            appLogger.logSystemEvent("Application started");
            appLogger.logUserAction("user123", "login");
            appLogger.logPerformanceMetric("response_time", 0.125);
            
            // 何らかのビジネスロジック
            performBusinessLogic();
            
        } catch (Exception e) {
            appLogger.logError("MainApplication", "Unexpected error", e);
        } finally {
            appLogger.logSystemEvent("Application shutdown");
        }
    }
    
    private static void performBusinessLogic() throws Exception {
        // ビジネスロジックのシミュレーション
        if (Math.random() > 0.7) {
            throw new Exception("ランダムなビジネス例外");
        }
    }
}

実用例(Webアプリケーション統合)

import java.util.logging.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// logging.properties設定ファイル例(コメントとして記載)
/*
# logging.properties
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# ルートロガー設定
.level=INFO

# ファイルハンドラー設定
java.util.logging.FileHandler.pattern=logs/webapp_%g.log
java.util.logging.FileHandler.limit=50000000
java.util.logging.FileHandler.count=5
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level=ALL

# コンソールハンドラー設定
java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# 個別ロガー設定
com.example.webapp.level=FINE
com.example.webapp.security.level=ALL
*/

public class WebApplicationLogging {
    
    // 各コンポーネント用のロガー
    private static final Logger ACCESS_LOGGER = Logger.getLogger("webapp.access");
    private static final Logger SECURITY_LOGGER = Logger.getLogger("webapp.security");
    private static final Logger PERFORMANCE_LOGGER = Logger.getLogger("webapp.performance");
    private static final Logger ERROR_LOGGER = Logger.getLogger("webapp.error");
    
    static {
        // 静的初期化でログ設定を行う
        initializeLogging();
    }
    
    private static void initializeLogging() {
        try {
            // システムプロパティからログ設定を読み込み
            String configFile = System.getProperty("java.util.logging.config.file");
            if (configFile != null) {
                LogManager.getLogManager().readConfiguration(
                    new FileInputStream(configFile));
            }
        } catch (Exception e) {
            System.err.println("ログ設定初期化エラー: " + e.getMessage());
        }
    }
    
    // HTTPリクエストロギング
    public static void logHttpRequest(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    long processingTime) {
        String clientIp = getClientIpAddress(request);
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String query = request.getQueryString();
        int statusCode = response.getStatus();
        
        String logMessage = String.format(
            "HTTP_REQUEST: %s %s %s%s %d %dms",
            clientIp, method, uri, 
            (query != null ? "?" + query : ""), 
            statusCode, processingTime
        );
        
        // アクセスログ
        ACCESS_LOGGER.info(logMessage);
        
        // パフォーマンス警告
        if (processingTime > 1000) {
            PERFORMANCE_LOGGER.warning(
                String.format("Slow request detected: %s took %dms", uri, processingTime));
        }
        
        // エラーログ
        if (statusCode >= 500) {
            ERROR_LOGGER.severe(String.format("Server error: %d for %s", statusCode, uri));
        } else if (statusCode >= 400) {
            ERROR_LOGGER.warning(String.format("Client error: %d for %s", statusCode, uri));
        }
    }
    
    // セキュリティイベントロギング
    public static void logSecurityEvent(String eventType, String details, 
                                      HttpServletRequest request) {
        String clientIp = getClientIpAddress(request);
        String userAgent = request.getHeader("User-Agent");
        String sessionId = request.getSession(false) != null ? 
            request.getSession().getId() : "no-session";
        
        String logMessage = String.format(
            "SECURITY_EVENT: %s | IP: %s | Session: %s | UA: %s | Details: %s",
            eventType, clientIp, sessionId, userAgent, details
        );
        
        SECURITY_LOGGER.warning(logMessage);
    }
    
    // エラーロギング
    public static void logApplicationError(String component, String operation, 
                                         Throwable error, Object... context) {
        StringBuilder contextStr = new StringBuilder();
        for (int i = 0; i < context.length; i += 2) {
            if (i + 1 < context.length) {
                contextStr.append(context[i]).append("=").append(context[i + 1]).append(" ");
            }
        }
        
        String logMessage = String.format(
            "APP_ERROR: [%s.%s] %s | Context: %s",
            component, operation, error.getMessage(), contextStr.toString().trim()
        );
        
        ERROR_LOGGER.log(Level.SEVERE, logMessage, error);
    }
    
    // ビジネスイベントロギング
    public static void logBusinessEvent(String eventType, String userId, 
                                      String details) {
        String logMessage = String.format(
            "BUSINESS_EVENT: %s | User: %s | Details: %s",
            eventType, userId, details
        );
        
        ACCESS_LOGGER.info(logMessage);
    }
    
    // パフォーマンスメトリクスロギング
    public static void logPerformanceMetric(String metricName, double value, 
                                          String unit) {
        if (PERFORMANCE_LOGGER.isLoggable(Level.CONFIG)) {
            String logMessage = String.format(
                "PERFORMANCE_METRIC: %s = %.3f %s",
                metricName, value, unit
            );
            PERFORMANCE_LOGGER.config(logMessage);
        }
    }
    
    private static String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
    
    // 使用例(サーブレットでの使用)
    public static void main(String[] args) {
        // サンプル使用例
        
        // システム起動ログ
        ACCESS_LOGGER.info("SYSTEM_STARTUP: Web application started successfully");
        
        // ビジネスイベント
        logBusinessEvent("USER_REGISTRATION", "user123", "新規ユーザー登録完了");
        
        // セキュリティイベント(模擬)
        // logSecurityEvent("INVALID_LOGIN", "5回連続失敗", mockRequest);
        
        // パフォーマンスメトリクス
        logPerformanceMetric("database_connection_pool_usage", 0.75, "ratio");
        
        // エラーイベント
        try {
            throw new RuntimeException("データベース接続エラー");
        } catch (Exception e) {
            logApplicationError("DatabaseService", "getConnection", e, 
                              "database", "primary", "retry_count", 3);
        }
        
        // システム終了ログ
        ACCESS_LOGGER.info("SYSTEM_SHUTDOWN: Web application shutting down gracefully");
    }
}