log4go
Java log4jからインスパイアされたGoロギングライブラリ。Java開発者に馴染みのある設定方式とAPI設計を提供。アペンダー、レイアウト、フィルター等の概念をGoに適用した伝統的な構成ベースのロギングフレームワーク。
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
ライセンス:-
トピックス
なし
スター履歴
データ取得日時: 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)
}
}