Apache Log4j (Legacy)

Apache財団による歴史あるJavaロギングフレームワーク。多くのJavaアプリケーションで長年使用されてきたが、セキュリティ脆弱性(Log4Shell)の発見により、現在は Log4j2 への移行が強く推奨されている。

ロギングJavaJakarta EEApacheエンタープライズデバッグ

GitHub概要

apache/logging-log4j1

Apache log4j1

スター868
ウォッチ110
フォーク576
作成日:2009年5月21日
言語:Java
ライセンス:Apache License 2.0

トピックス

log4j

スター履歴

apache/logging-log4j1 Star History
データ取得日時: 2025/10/22 04:10

ライブラリ

Apache Log4j

概要

Apache Log4jはJava界で最も歴史と実績のあるロギングライブラリです。「汎用性が高く、機能豊富、効率的なJavaロギングAPI」として設計され、スタンドアロンアプリケーションからエンタープライズシステムまで幅広い用途で使用されています。ファイル、ネットワーク、データベース、SMTP等への豊富な出力先と柔軟なフォーマット機能を提供します。

詳細

Apache Log4j 2.25.0は2025年6月にリリースされた最新版で、GraalVMネイティブイメージの完全サポートやパフォーマンス改善が含まれています。モジュラー設計によりAPI、実装、デプロイメント支援コンポーネントに分かれており、高い拡張性とカスタマイズ性を実現。Java 8以上をサポートし、モダンなJavaアプリケーション開発で幅広く使用されています。

主な特徴

  • 豊富なAppender: ファイル、ネットワーク、データベース、SMTP、JMS等多数の出力先
  • 柔軟なLayout: CSV、HTML、JSON、Syslog等のフォーマットサポート
  • 高性能なフィルター: レートベース、正規表現、スクリプト、時間等による高度なフィルタリング
  • 動的設定リロード: 設定ファイル変更時の自動リロード(ログイベント故失なし)
  • GraalVMサポート: ネイティブイメージ生成の完全サポート
  • ガベージコレクターフリー: ガベージコレクションに負担をかけない高性能設計

メリット・デメリット

メリット

  • Java界で最も歴史と実績のあるロガー(20年以上の開発実績)
  • Apache Software Foundationによる安定したガバナンスとサポート
  • 豊富なエコシステム(Spring Boot、Hibernate等との連携)
  • エンタープライズグレードの機能(非同期ロギング、クラスタリング等)
  • 高性能でスケーラブル(ガベージコレクターフリー設計)
  • 充実したドキュメントと活発なコミュニティ

デメリット

  • 初期設定が複雑で学習コストが高い
  • XML設定ファイルが詳細で管理が難しい場合がある
  • モダンなJavaのアノテーションベース設定が他ライブラリより遅れている
  • デバッグ時のエラーメッセージが分かりにくいことがある
  • 多機能ゆえのメモリ使用量がやや多い
  • 軽量なログ出力にはオーバースペックの場合がある

参考ページ

書き方の例

基本的なセットアップ

<!-- Maven依存関係 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.25.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.25.0</version>
</dependency>

<!-- SLF4Jブリッジ(既存アプリでSLF4J使用時) -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.25.0</version>
</dependency>
// Gradle依存関係
implementation 'org.apache.logging.log4j:log4j-core:2.25.0'
implementation 'org.apache.logging.log4j:log4j-api:2.25.0'

シンプルなロガー使用

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class MyApp {
    // ロガーの取得
    private static final Logger logger = LogManager.getLogger(MyApp.class);
    
    public static void main(String[] args) {
        // 基本的なログ出力
        logger.trace("トレースメッセージ");
        logger.debug("デバッグメッセージ");
        logger.info("情報メッセージ");
        logger.warn("警告メッセージ");
        logger.error("エラーメッセージ");
        
        // パラメータ付きログ出力
        String userId = "user123";
        int age = 25;
        logger.info("ユーザー情報: ID={}, 年齢={}", userId, age);
        
        // 例外ログ出力
        try {
            int result = 10 / 0;
        } catch (Exception e) {
            logger.error("計算エラーが発生しました", e);
        }
    }
}

基本的なXML設定(log4j2.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <!-- コンソール出力 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        
        <!-- ファイル出力 -->
        <File name="FileAppender" fileName="logs/app.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </File>
        
        <!-- ローリングファイル -->
        <RollingFile name="RollingFileAppender" fileName="logs/app.log" 
                     filePattern="logs/app.%d{yyyy-MM-dd}.%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <!-- 特定パッケージのログレベル設定 -->
        <Logger name="com.example.database" level="DEBUG" additivity="false">
            <AppenderRef ref="FileAppender"/>
        </Logger>
        
        <!-- ルートロガー -->
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFileAppender"/>
        </Root>
    </Loggers>
</Configuration>

非同期ロギング設定

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <!-- 非同期ロガー設定 -->
    <AsyncLogger name="com.example.performance" level="INFO" additivity="false">
        <AppenderRef ref="FileAppender"/>
    </AsyncLogger>
    
    <!-- 完全非同期モード -->
    <AsyncRoot level="INFO">
        <AppenderRef ref="Console"/>
        <AppenderRef ref="FileAppender"/>
    </AsyncRoot>
    
    <!-- LMAX Disruptor依存関係が必要 -->
    <!-- 
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.4</version>
    </dependency>
    -->
</Configuration>

プログラマティック設定

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.builder.api.*;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;

public class ProgrammaticConfig {
    public static void main(String[] args) {
        // Configuration Builderを使用したプログラマティック設定
        ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
        
        // Console Appenderの設定
        AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "CONSOLE")
            .addAttribute("target", "SYSTEM_OUT")
            .add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"));
        
        // File Appenderの設定
        AppenderComponentBuilder fileAppender = builder.newAppender("File", "File")
            .addAttribute("fileName", "logs/programmatic.log")
            .add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"));
        
        builder.add(consoleAppender);
        builder.add(fileAppender);
        
        // Root Loggerの設定
        RootLoggerComponentBuilder rootLogger = builder.newRootLogger("INFO")
            .add(builder.newAppenderRef("Console"))
            .add(builder.newAppenderRef("File"));
        
        builder.add(rootLogger);
        
        // 設定の適用
        LoggerContext context = (LoggerContext) LogManager.getContext(false);
        Configuration config = builder.build();
        context.start(config);
        
        // ロガーの使用
        Logger logger = LogManager.getLogger(ProgrammaticConfig.class);
        logger.info("プログラマティック設定でのログ出力");
    }
}

フィルターとマーカーの使用

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- レベルフィルター -->
            <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
        </Console>
        
        <File name="ErrorFile" fileName="logs/error.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- エラーレベルのみ -->
            <LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
        </File>
        
        <File name="SecurityFile" fileName="logs/security.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- マーカーフィルター -->
            <MarkerFilter marker="SECURITY" onMatch="ACCEPT" onMismatch="DENY"/>
        </File>
    </Appenders>
    
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="ErrorFile"/>
            <AppenderRef ref="SecurityFile"/>
        </Root>
    </Loggers>
</Configuration>
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

public class FilterExample {
    private static final Logger logger = LogManager.getLogger(FilterExample.class);
    private static final Marker SECURITY_MARKER = MarkerManager.getMarker("SECURITY");
    private static final Marker PERFORMANCE_MARKER = MarkerManager.getMarker("PERFORMANCE");
    
    public static void main(String[] args) {
        // 通常のログ
        logger.info("アプリケーションが開始されました");
        
        // セキュリティマーナー付きログ(security.logに出力される)
        logger.warn(SECURITY_MARKER, "不正なアクセス試行を検知しました: IP={}", "192.168.1.100");
        
        // パフォーマンスマーカー付きログ
        logger.debug(PERFORMANCE_MARKER, "DBクエリ実行時間: {}ms", 150);
        
        // エラーログ(error.logに出力される)
        logger.error("データベース接続エラーが発生しました");
    }
}

Spring Boot統合

<!-- Spring Bootではdefaultでlogbackが使用されるため、Log4j2に変更 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
# application.yml
logging:
  config: classpath:log4j2-spring.xml
  level:
    com.example: DEBUG
    org.springframework: INFO
    root: WARN
// Spring Bootアプリケーションでの使用
@RestController
@Slf4j  // Lombokを使用する場合
public class UserController {
    private static final Logger logger = LogManager.getLogger(UserController.class);
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        logger.info("ユーザー情報取得リクエスト: userId={}", id);
        
        try {
            User user = userService.findById(id);
            logger.debug("ユーザー情報取得成功: {}", user);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            logger.warn("ユーザーが見つかりません: userId={}", id);
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            logger.error("ユーザー情報取得中にエラーが発生しました: userId={}", id, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}