log4go

Java log4jからインスパイアされたGoロギングライブラリ。Java開発者に馴染みのある設定方式とAPI設計を提供。アペンダー、レイアウト、フィルター等の概念をGoに適用した伝統的な構成ベースのロギングフレームワーク。

ロギングGo設定ベースJava-inspiredレベル別ログアペンダー

GitHub概要

jeanphorn/log4go

a logging package for golang similar to log4j or log4c++ supporting console, file and network.

スター137
ウォッチ7
フォーク75
作成日:2017年11月19日
言語:Go
ライセンス:-

トピックス

なし

スター履歴

jeanphorn/log4go Star History
データ取得日時: 2025/7/17 08:20

ライブラリ

log4go

概要

log4goは「Java log4jからインスパイアされたGoロギングライブラリ」として開発された、設定ベースのロギングフレームワークです。Java開発者に馴染みのある設定方式とAPI設計を提供し、アペンダー、レイアウト、フィルター等の概念をGoに適用した伝統的な構成ベースのロギングフレームワーク。レベル別ロギングと高度な設定可能性により、複雑なログ要件に対応できる設計となっています。

詳細

log4go 2025年版はGoエコシステムにおけるJava log4jライクなロギング体験を提供する特殊なライブラリです。複数の実装が存在し、alecthomas/log4goを基にした様々なフォークが開発されている状況で、各実装が独自の拡張機能を提供しています。XML・JSON設定ファイルサポート、カテゴリ別出力、ログローテーション機能など、エンタープライズレベルのログ管理機能を搭載。Java開発背景を持つチームでの採用例が見られますが、現代的なGo開発では他の選択肢が優先される傾向にあります。

主な特徴

  • Java log4j互換API: Java開発者に馴染みのある設定方式とAPI設計
  • レベル別ログ制御: Finest、Fine、Debug、Trace、Info、Warning、Error、Critical
  • 多様な出力先: コンソール、ファイル、ネットワーク出力をサポート
  • 設定ファイル対応: XML・JSON形式の設定ファイルによる詳細制御
  • ログローテーション: サイズ・時間ベースのログファイル管理
  • カテゴリ別処理: 異なるカテゴリごとの出力制御

メリット・デメリット

メリット

  • Java log4jと類似のAPI設計により、Java開発者にとって学習コストが低い
  • 豊富な設定オプションと柔軟なアペンダーシステム
  • XMLやJSON設定ファイルによる外部設定管理が可能
  • カテゴリ別の詳細なログレベル制御
  • ファイルローテーションと多様な出力先への同時出力
  • Enterprise環境での複雑なログ要件に対応可能

デメリット

  • 現代的なGo慣例への対応が不十分で、メンテナンス性に課題
  • パフォーマンス面でslog、Zap、Zerologに劣る
  • 公式ドキュメントが不十分で、利用例が限定的
  • 複数のフォークが存在し、どれを選ぶべきか判断が困難
  • 新規プロジェクトでの推奨度が低く、コミュニティサポートが限定的
  • Go標準ライブラリslogの登場により、存在意義が薄れている

参考ページ

書き方の例

インストールと基本セットアップ

# log4goのインストール(alecthomas版)
go get github.com/alecthomas/log4go

# または拡張版(jeanphorn版)
go get github.com/jeanphorn/log4go

# プロジェクトでの依存関係確認
go mod tidy

# Go環境での確認
go list -m github.com/alecthomas/log4go

基本的なロガー作成と使用

package main

import (
    "time"
    l4g "github.com/alecthomas/log4go"
)

func main() {
    // 新しいLoggerを作成
    log := l4g.NewLogger()
    defer log.Close()

    // コンソールアペンダーを追加(DEBUGレベル以上)
    log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())

    // ファイルアペンダーを追加(INFOレベル以上)
    log.AddFilter("file", l4g.INFO, l4g.NewFileLogWriter("application.log", true))

    // ログレベル別の出力例
    log.Finest("最も詳細な情報: %s", "データベース接続詳細")
    log.Fine("詳細情報: %s", "リクエスト処理開始")
    log.Debug("デバッグ情報: %s", "変数の値確認")
    log.Trace("トレース情報: %s", "関数呼び出し")
    log.Info("情報: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
    log.Warn("警告: %s", "メモリ使用量が80%を超過")
    log.Error("エラー: %s", "データベース接続失敗")
    log.Critical("致命的エラー: %s", "システム停止")

    // 構造化ログ(メッセージとプロパティ)
    log.Info("ユーザーログイン: ユーザーID=%d, IP=%s", 12345, "192.168.1.100")
    log.Error("処理失敗: タスクID=%s, エラー=%v", "task-001", "timeout error")
}

XML設定ファイルによる高度な設定

<!-- log4go.xml -->
<logging>
  <filter enabled="true">
    <tag>stdout</tag>
    <type>console</type>
    <level>DEBUG</level>
  </filter>

  <filter enabled="true">
    <tag>file</tag>
    <type>file</type>
    <level>INFO</level>
    <property name="filename">logs/application.log</property>
    <property name="format">[%D %T] [%L] (%S) %M</property>
    <property name="rotate">true</property>
    <property name="maxsize">100M</property>
    <property name="maxlines">1000000</property>
    <property name="daily">true</property>
  </filter>

  <filter enabled="true">
    <tag>error_file</tag>
    <type>file</type>
    <level>ERROR</level>
    <property name="filename">logs/error.log</property>
    <property name="format">[%D %T] [%L] (%S) %M</property>
  </filter>

  <filter enabled="true">
    <tag>network</tag>
    <type>socket</type>
    <level>WARN</level>
    <property name="endpoint">192.168.1.200:9999</property>
    <property name="protocol">udp</property>
  </filter>
</logging>
package main

import (
    l4g "github.com/alecthomas/log4go"
)

func main() {
    // XML設定ファイルからロガー設定を読み込み
    l4g.LoadConfiguration("log4go.xml")
    defer l4g.Close()

    // グローバルロガーを使用
    l4g.Debug("アプリケーション開始")
    l4g.Info("設定ファイル読み込み完了")
    l4g.Warn("警告: 設定値がデフォルトと異なります")
    l4g.Error("エラー: 外部APIアクセス失敗")

    // カスタムロガーを作成して使用
    customLog := l4g.NewLogger()
    customLog.LoadConfiguration("custom-log4go.xml")
    defer customLog.Close()

    customLog.Info("カスタムロガーによる出力")
}

ログローテーションとファイル管理

package main

import (
    "time"
    l4g "github.com/alecthomas/log4go"
)

func main() {
    log := l4g.NewLogger()
    defer log.Close()

    // ローテーション付きファイルロガー
    flw := l4g.NewFileLogWriter("logs/rotating.log", true)
    
    // ローテーション設定
    flw.SetRotateSize(1024 * 1024)     // 1MBでローテーション
    flw.SetRotateLines(10000)          // 10,000行でローテーション
    flw.SetRotateDaily(true)           // 日次ローテーション
    
    log.AddFilter("rotating_file", l4g.INFO, flw)

    // ネットワークロガー(syslog風)
    socketWriter := l4g.NewSocketLogWriter("tcp", "192.168.1.100:514")
    log.AddFilter("network", l4g.WARN, socketWriter)

    // 複数出力への同時ログ出力
    for i := 0; i < 100; i++ {
        log.Info("ログ出力テスト: 番号=%d, タイムスタンプ=%s", 
                 i, time.Now().Format(time.RFC3339))
        
        if i%10 == 0 {
            log.Warn("10件ごとの警告ログ: 進捗=%d%%", i)
        }
        
        time.Sleep(100 * time.Millisecond)
    }
    
    log.Info("ログ出力完了")
}

カテゴリ別ログ制御とフィルタリング

package main

import (
    l4g "github.com/alecthomas/log4go"
)

// カテゴリ別ロガーの管理
type LoggerManager struct {
    appLogger    l4g.Logger
    dbLogger     l4g.Logger
    apiLogger    l4g.Logger
    systemLogger l4g.Logger
}

func NewLoggerManager() *LoggerManager {
    lm := &LoggerManager{
        appLogger:    l4g.NewLogger(),
        dbLogger:     l4g.NewLogger(),
        apiLogger:    l4g.NewLogger(),
        systemLogger: l4g.NewLogger(),
    }

    // アプリケーションログ
    lm.appLogger.AddFilter("app_console", l4g.INFO, l4g.NewConsoleLogWriter())
    lm.appLogger.AddFilter("app_file", l4g.DEBUG, 
        l4g.NewFileLogWriter("logs/application.log", true))

    // データベースログ(詳細レベル)
    lm.dbLogger.AddFilter("db_file", l4g.FINE, 
        l4g.NewFileLogWriter("logs/database.log", true))

    // APIログ(リクエスト/レスポンス)
    lm.apiLogger.AddFilter("api_file", l4g.INFO, 
        l4g.NewFileLogWriter("logs/api.log", true))

    // システムログ(エラーのみ)
    lm.systemLogger.AddFilter("system_file", l4g.ERROR, 
        l4g.NewFileLogWriter("logs/system.log", true))

    return lm
}

func (lm *LoggerManager) Close() {
    lm.appLogger.Close()
    lm.dbLogger.Close()
    lm.apiLogger.Close()
    lm.systemLogger.Close()
}

func main() {
    logManager := NewLoggerManager()
    defer logManager.Close()

    // カテゴリ別ログ出力
    logManager.appLogger.Info("アプリケーション開始")
    logManager.dbLogger.Fine("データベース接続: host=%s", "localhost:5432")
    logManager.apiLogger.Info("API リクエスト: GET /api/users")
    logManager.systemLogger.Error("システムエラー: メモリ不足")

    // 業務処理のシミュレーション
    processUserRequest(logManager)
    processDatabaseQuery(logManager)
    processAPICall(logManager)
}

func processUserRequest(lm *LoggerManager) {
    lm.appLogger.Info("ユーザーリクエスト処理開始")
    lm.appLogger.Debug("セッション確認: ユーザーID=12345")
    lm.appLogger.Info("ユーザーリクエスト処理完了")
}

func processDatabaseQuery(lm *LoggerManager) {
    lm.dbLogger.Fine("クエリ実行開始: SELECT * FROM users WHERE active = 1")
    lm.dbLogger.Debug("クエリ実行時間: 120ms")
    lm.dbLogger.Info("クエリ実行完了: 結果件数=1500")
}

func processAPICall(lm *LoggerManager) {
    lm.apiLogger.Info("外部API呼び出し: https://api.example.com/data")
    lm.apiLogger.Warn("API応答遅延: 応答時間=5.2秒")
    lm.apiLogger.Info("API呼び出し完了: ステータス=200")
}

エラーハンドリングとパフォーマンス考慮

package main

import (
    "errors"
    "fmt"
    "runtime"
    "time"
    l4g "github.com/alecthomas/log4go"
)

func main() {
    log := l4g.NewLogger()
    defer log.Close()

    // 非同期ファイル書き込み設定
    flw := l4g.NewFileLogWriter("logs/performance.log", true)
    log.AddFilter("perf_file", l4g.INFO, flw)

    // エラーハンドリング付きログ操作
    if err := safeLogging(log); err != nil {
        fmt.Printf("ログ操作エラー: %v\n", err)
    }

    // パフォーマンステスト
    performanceTest(log)

    // メモリ使用量の監視
    monitorMemoryUsage(log)
}

func safeLogging(log l4g.Logger) error {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("ログ操作でパニック発生: %v\n", r)
        }
    }()

    // 様々なデータ型のログ出力
    log.Info("文字列ログ: %s", "テストメッセージ")
    log.Info("数値ログ: %d", 12345)
    log.Info("浮動小数点ログ: %.2f", 123.456)
    log.Info("ブール値ログ: %t", true)

    // エラー情報の詳細ログ
    err := errors.New("サンプルエラー")
    log.Error("エラーが発生しました: %v", err)
    
    // スタックトレース情報(手動取得)
    _, file, line, ok := runtime.Caller(0)
    if ok {
        log.Debug("呼び出し元情報: ファイル=%s, 行=%d", file, line)
    }

    return nil
}

func performanceTest(log l4g.Logger) {
    start := time.Now()
    
    // 大量ログ出力のパフォーマンステスト
    for i := 0; i < 1000; i++ {
        log.Info("パフォーマンステスト: 番号=%d", i)
        
        if i%100 == 0 {
            elapsed := time.Since(start)
            log.Debug("進捗: %d/1000, 経過時間=%v", i, elapsed)
        }
    }
    
    totalElapsed := time.Since(start)
    log.Info("パフォーマンステスト完了: 総実行時間=%v", totalElapsed)
}

func monitorMemoryUsage(log l4g.Logger) {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    
    log.Info("メモリ使用量: Alloc=%d KB, TotalAlloc=%d KB, Sys=%d KB", 
             m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024)
    log.Info("GC実行回数: %d回", m.NumGC)
    log.Info("Goroutine数: %d", runtime.NumGoroutine())
}

実用的なアプリケーション統合例

package main

import (
    "fmt"
    "net/http"
    "os"
    "time"
    l4g "github.com/alecthomas/log4go"
)

// アプリケーション全体のロガー設定
type Application struct {
    logger l4g.Logger
    server *http.Server
}

func NewApplication() *Application {
    app := &Application{}
    app.setupLogging()
    app.setupHTTPServer()
    return app
}

func (app *Application) setupLogging() {
    app.logger = l4g.NewLogger()
    
    // 環境変数によるログレベル制御
    logLevel := l4g.INFO
    if level := os.Getenv("LOG_LEVEL"); level != "" {
        switch level {
        case "DEBUG":
            logLevel = l4g.DEBUG
        case "WARN":
            logLevel = l4g.WARNING
        case "ERROR":
            logLevel = l4g.ERROR
        }
    }
    
    // コンソール出力(開発環境)
    if os.Getenv("ENV") == "development" {
        app.logger.AddFilter("console", logLevel, l4g.NewConsoleLogWriter())
    }
    
    // ファイル出力(本番環境)
    app.logger.AddFilter("file", logLevel, 
        l4g.NewFileLogWriter("logs/app.log", true))
    
    // エラー専用ファイル
    app.logger.AddFilter("error_file", l4g.ERROR, 
        l4g.NewFileLogWriter("logs/error.log", true))
    
    app.logger.Info("ロガー設定完了: ログレベル=%v", logLevel)
}

func (app *Application) setupHTTPServer() {
    mux := http.NewServeMux()
    
    // ミドルウェア付きのハンドラー
    mux.HandleFunc("/api/users", app.withLogging(app.handleUsers))
    mux.HandleFunc("/api/health", app.withLogging(app.handleHealth))
    
    app.server = &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
}

func (app *Application) withLogging(handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        app.logger.Info("HTTP リクエスト開始: %s %s from %s", 
                        r.Method, r.URL.Path, r.RemoteAddr)
        
        // リクエスト処理
        handler(w, r)
        
        elapsed := time.Since(start)
        app.logger.Info("HTTP リクエスト完了: %s %s, 処理時間=%v", 
                        r.Method, r.URL.Path, elapsed)
    }
}

func (app *Application) handleUsers(w http.ResponseWriter, r *http.Request) {
    app.logger.Debug("ユーザー一覧取得処理開始")
    
    // ビジネスロジックのシミュレーション
    time.Sleep(100 * time.Millisecond)
    
    response := `{"users": [{"id": 1, "name": "田中太郎"}]}`
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(response))
    
    app.logger.Debug("ユーザー一覧取得処理完了")
}

func (app *Application) handleHealth(w http.ResponseWriter, r *http.Request) {
    app.logger.Debug("ヘルスチェック処理")
    
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"status": "healthy"}`))
}

func (app *Application) Start() error {
    app.logger.Info("HTTPサーバー開始: アドレス=%s", app.server.Addr)
    return app.server.ListenAndServe()
}

func (app *Application) Shutdown() {
    app.logger.Info("アプリケーション終了処理開始")
    app.logger.Close()
}

func main() {
    app := NewApplication()
    defer app.Shutdown()
    
    app.logger.Info("アプリケーション開始")
    
    if err := app.Start(); err != nil {
        app.logger.Error("サーバー開始エラー: %v", err)
        fmt.Printf("Fatal error: %v\n", err)
        os.Exit(1)
    }
}