FLog
クイックでシンプルなロギングソリューションを提供する高度なロギングフレームワーク。全ログをデータベースに保存し、zipファイルとしてエクスポート可能。本番アプリでのログ収集と分析に特化した機能を提供。
ライブラリ
Flog
概要
Flogは「Go言語向けのシンプルで高速なログライブラリ」として、複数の実装が存在する多様なログソリューションです。Facebook版はglogから派生したライブラリ向け最適化ログライブラリ、Coder版は人間が読みやすいフォーマット重視のCLIアプリ向け軽量ログ、jellynian版はシンプルさとパフォーマンスを追求した高速ログと、それぞれ異なる特徴と用途を持ちます。
詳細
Flogライブラリ群は Go 言語のログライブラリエコシステムにおいて、異なるニーズに対応するように発展してきました。Facebook(現 Meta)の flog はライブラリ内部での使用を意識して glog を改善したバージョンで、環境変数設定対応、flag.Parse()不要の即座利用、DEBUGとCRITICALレベル追加などの機能を提供します。Coder 版は CLI アプリケーションでの使用を想定し、人間が読みやすい美しいフォーマットとミニマルなデザインを重視しています。jellynian 版はシンプルさと高速性を特徴とし、基本的なログ機能を軽量に実装しています。
主な特徴
- 多様な実装: Facebook、Coder、jellynian版の3つの主要実装
- 柔軟な設定: 環境変数や構造体での設定可能
- 即座利用: flag.Parse()不要で即座にログ出力開始
- 拡張ログレベル: DEBUGとCRITICALレベルの追加サポート
- フォーマット最適化: CLIや人間可読性を重視した出力
- 高パフォーマンス: 軽量で高速なログ処理
メリット・デメリット
メリット
- Goネイティブな実装で高パフォーマンスと組み込み容易性
- 複数の実装からプロジェクトニーズに最適な選択が可能
- Facebook版はライブラリ内組み込みでの利用に最適化
- Coder版は CLI アプリでの人間可読性と美しい出力を実現
- jellynian版はシンプルさと高速性で基本的なニーズを満たす
- 環境変数設定や即座利用可能な柔軟性
デメリット
- 同名ライブラリが複数存在し選択時の混乱と調査コスト
- 各実装でAPIや機能が異なり移植性や互換性の問題
- コミュニティサポートが分散し統一的な情報収集の困難
- 複雑なログ管理機能(ローテーション、集約等)の不足
- 本格的なエンタープライズログ管理では機能不足の可能性
- ドキュメントや例が各実装で異なり学習コストが増大
参考ページ
書き方の例
基本セットアップ
// Facebook Flog (facebookincubator/flog)
package main
import (
"github.com/facebookincubator/flog"
)
func main() {
// 設定なしで即座使用可能
flog.Info("Application started")
// 環境変数でログレベル設定
// export FLOG_LEVEL=DEBUG
flog.Debug("Debug information")
flog.Critical("Critical system error")
}
// Coder Flog (coder/flog)
package main
import (
"go.coder.com/flog"
)
func main() {
// 人間可読性を重視したフォーマット
flog.Info("Application started successfully")
flog.Warn("Memory usage is high")
flog.Error("Failed to connect to database")
}
// jellynian Flog (jellynian/flog)
package main
import (
"github.com/jellynian/flog"
)
func main() {
// シンプルで高速なログ出力
flog.Println("Simple log message")
flog.Printf("User %s logged in", "john")
}
基本的なログ出力
// Facebook Flog - ライブラリ向け最適化
package main
import (
"context"
"github.com/facebookincubator/flog"
)
func processUser(ctx context.Context, userID string) {
// コンテキスト対応ログ
flog.InfoContext(ctx, "Processing user", "user_id", userID)
// ログレベル別出力
flog.Debug("Detailed debug information")
flog.Info("General information")
flog.Warning("Warning message")
flog.Error("Error occurred")
flog.Critical("Critical system failure")
// 構造化ログ
flog.Info("User operation",
"user_id", userID,
"action", "login",
"timestamp", time.Now(),
"ip", "192.168.1.100",
)
}
// 設定のカスタマイズ
func configureLogging() {
config := flog.Config{
Level: flog.LevelInfo,
Format: flog.FormatJSON, // JSON形式で出力
}
flog.Configure(config)
}
func main() {
configureLogging()
ctx := context.Background()
processUser(ctx, "user-123")
}
高度な設定(フォーマット、フィルタリング)
// Coder Flog - CLIアプリ向け美しいフォーマット
package main
import (
"fmt"
"os"
"time"
"go.coder.com/flog"
)
// カスタムロガーの作成
func setupCustomLogger() *flog.Logger {
return flog.New(
flog.OutputHuman(), // 人間可読性を重視した出力
flog.LevelFilter(flog.LevelInfo), // INFO以上のみ表示
flog.TimeFormat(time.RFC3339), // 時刻フォーマット設定
)
}
// コンテキスト付きロガー
func createContextLogger(component string) *flog.Logger {
return flog.New(
flog.OutputHuman(),
flog.LevelFilter(flog.LevelDebug),
).With(
flog.F("component", component),
flog.F("version", "1.0.0"),
)
}
func main() {
// カスタムロガーの使用
logger := setupCustomLogger()
logger.Info("Starting application",
flog.F("pid", os.Getpid()),
flog.F("env", os.Getenv("ENV")),
)
// コンポーネント別ロガー
dbLogger := createContextLogger("database")
apiLogger := createContextLogger("api")
dbLogger.Info("Database connection established",
flog.F("host", "localhost"),
flog.F("port", 5432),
)
apiLogger.Warn("API rate limit approaching",
flog.F("current", 950),
flog.F("limit", 1000),
)
// エラーログにスタックトレースを含める
if err := processData(); err != nil {
logger.Error("Failed to process data",
flog.F("error", err),
flog.F("stack", fmt.Sprintf("%+v", err)),
)
}
}
func processData() error {
return fmt.Errorf("sample error for demonstration")
}
エラーハンドリング
// jellynian Flog - シンプル高速ログ
package main
import (
"errors"
"fmt"
"time"
"github.com/jellynian/flog"
)
// エラーハンドリング付きログ関数
func logWithErrorHandling(level, message string, args ...interface{}) {
defer func() {
if r := recover(); r != nil {
// パニック時のフォールバック
fmt.Printf("[PANIC] Logging failed: %v\n", r)
fmt.Printf("[FALLBACK] %s: %s\n", level, fmt.Sprintf(message, args...))
}
}()
switch level {
case "INFO":
flog.Printf("[INFO] "+message, args...)
case "WARN":
flog.Printf("[WARN] "+message, args...)
case "ERROR":
flog.Printf("[ERROR] "+message, args...)
default:
flog.Printf("[LOG] "+message, args...)
}
}
// リトライ機能付きログ
func logWithRetry(message string, maxRetries int) error {
var lastErr error
for i := 0; i <= maxRetries; i++ {
func() {
defer func() {
if r := recover(); r != nil {
lastErr = fmt.Errorf("log panic: %v", r)
}
}()
flog.Println(message)
lastErr = nil // 成功時はエラーをクリア
}()
if lastErr == nil {
return nil // 成功
}
if i < maxRetries {
time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
}
}
return fmt.Errorf("failed to log after %d retries: %w", maxRetries, lastErr)
}
// バッチログ処理
type LogEntry struct {
Level string
Message string
Timestamp time.Time
Fields map[string]interface{}
}
func processBatchLogs(entries []LogEntry) {
for _, entry := range entries {
safeLog := func(e LogEntry) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("[BATCH_LOG_ERROR] Failed to log entry: %v\n", r)
}
}()
timestamp := entry.Timestamp.Format("2006-01-02 15:04:05")
message := fmt.Sprintf("[%s] [%s] %s", timestamp, entry.Level, entry.Message)
if len(entry.Fields) > 0 {
message += fmt.Sprintf(" %+v", entry.Fields)
}
flog.Println(message)
}
safeLog(entry)
}
}
func main() {
// 安全なログ出力
logWithErrorHandling("INFO", "Application started at %v", time.Now())
logWithErrorHandling("WARN", "Memory usage: %d MB", 512)
// リトライ付きログ
if err := logWithRetry("Critical system message", 3); err != nil {
fmt.Printf("Final log failure: %v\n", err)
}
// バッチログ処理
entries := []LogEntry{
{
Level: "INFO",
Message: "User login",
Timestamp: time.Now(),
Fields: map[string]interface{}{"user_id": 123, "ip": "192.168.1.1"},
},
{
Level: "ERROR",
Message: "Payment failed",
Timestamp: time.Now(),
Fields: map[string]interface{}{"order_id": "ORD-456", "amount": 9800},
},
}
processBatchLogs(entries)
}
実用例(統合ログシステム)
// 複数Flog実装を統合したユニバーサルロガー
package main
import (
"context"
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
// 各 Flog 実装をインポート
facebooklog "github.com/facebookincubator/flog"
coderlog "go.coder.com/flog"
jellynianlog "github.com/jellynian/flog"
)
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
CRITICAL
)
type LoggerType int
const (
FacebookLogger LoggerType = iota
CoderLogger
JellynianLogger
)
// 統合ロガーインターフェース
type UniversalLogger struct {
loggerType LoggerType
level LogLevel
output io.Writer
mu sync.RWMutex
// 各ロガーのインスタンス
facebookLogger *facebooklog.Logger
coderLogger *coderlog.Logger
}
func NewUniversalLogger(loggerType LoggerType, level LogLevel) *UniversalLogger {
ul := &UniversalLogger{
loggerType: loggerType,
level: level,
output: os.Stderr,
}
switch loggerType {
case FacebookLogger:
// Facebook Flog の設定
config := facebooklog.Config{
Level: facebooklog.Level(level),
Format: facebooklog.FormatJSON,
}
facebooklog.Configure(config)
case CoderLogger:
// Coder Flog の設定
ul.coderLogger = coderlog.New(
coderlog.OutputHuman(),
coderlog.LevelFilter(coderlog.Level(level)),
coderlog.TimeFormat(time.RFC3339),
)
}
return ul
}
func (ul *UniversalLogger) log(ctx context.Context, level LogLevel, message string, fields ...interface{}) {
ul.mu.RLock()
defer ul.mu.RUnlock()
if level < ul.level {
return
}
// 呼び出し元情報を取得
_, file, line, _ := runtime.Caller(2)
// 共通フィールドを追加
commonFields := []interface{}{
"timestamp", time.Now(),
"file", file,
"line", line,
"goroutine", runtime.NumGoroutine(),
}
allFields := append(commonFields, fields...)
switch ul.loggerType {
case FacebookLogger:
switch level {
case DEBUG:
facebooklog.DebugContext(ctx, message, allFields...)
case INFO:
facebooklog.InfoContext(ctx, message, allFields...)
case WARN:
facebooklog.WarningContext(ctx, message, allFields...)
case ERROR:
facebooklog.ErrorContext(ctx, message, allFields...)
case CRITICAL:
facebooklog.CriticalContext(ctx, message, allFields...)
}
case CoderLogger:
logger := ul.coderLogger
for i := 0; i < len(allFields); i += 2 {
if i+1 < len(allFields) {
logger = logger.With(coderlog.F(fmt.Sprint(allFields[i]), allFields[i+1]))
}
}
switch level {
case DEBUG:
logger.Debug(message)
case INFO:
logger.Info(message)
case WARN:
logger.Warn(message)
case ERROR:
logger.Error(message)
case CRITICAL:
logger.Error("[CRITICAL] " + message)
}
case JellynianLogger:
// jellynian flog はシンプルな Printf スタイル
levelStr := map[LogLevel]string{
DEBUG: "DEBUG",
INFO: "INFO",
WARN: "WARN",
ERROR: "ERROR",
CRITICAL: "CRITICAL",
}
timestamp := time.Now().Format("2006-01-02 15:04:05")
logLine := fmt.Sprintf("[%s] [%s] %s", timestamp, levelStr[level], message)
if len(fields) > 0 {
logLine += fmt.Sprintf(" %+v", fields)
}
jellynianlog.Println(logLine)
}
}
// コンビニエンスメソッド
func (ul *UniversalLogger) Debug(ctx context.Context, message string, fields ...interface{}) {
ul.log(ctx, DEBUG, message, fields...)
}
func (ul *UniversalLogger) Info(ctx context.Context, message string, fields ...interface{}) {
ul.log(ctx, INFO, message, fields...)
}
func (ul *UniversalLogger) Warn(ctx context.Context, message string, fields ...interface{}) {
ul.log(ctx, WARN, message, fields...)
}
func (ul *UniversalLogger) Error(ctx context.Context, message string, fields ...interface{}) {
ul.log(ctx, ERROR, message, fields...)
}
func (ul *UniversalLogger) Critical(ctx context.Context, message string, fields ...interface{}) {
ul.log(ctx, CRITICAL, message, fields...)
}
// パフォーマンスモニタリング機能
func (ul *UniversalLogger) WithTiming(ctx context.Context, operation string, fn func() error) error {
start := time.Now()
ul.Debug(ctx, "Operation started", "operation", operation)
err := fn()
duration := time.Since(start)
if err != nil {
ul.Error(ctx, "Operation failed",
"operation", operation,
"duration", duration,
"error", err,
)
} else {
ul.Info(ctx, "Operation completed",
"operation", operation,
"duration", duration,
)
}
return err
}
func main() {
// 各ロガーのテスト
ctx := context.Background()
// Facebook Flog でテスト
fbLogger := NewUniversalLogger(FacebookLogger, DEBUG)
fbLogger.Info(ctx, "Testing Facebook Flog", "component", "main")
// Coder Flog でテスト
coderLogger := NewUniversalLogger(CoderLogger, INFO)
coderLogger.Warn(ctx, "Testing Coder Flog", "component", "main")
// jellynian Flog でテスト
jellynianLogger := NewUniversalLogger(JellynianLogger, DEBUG)
jellynianLogger.Error(ctx, "Testing jellynian Flog", "component", "main")
// パフォーマンスモニタリングのテスト
err := fbLogger.WithTiming(ctx, "database_query", func() error {
time.Sleep(100 * time.Millisecond) // ダミー処理
return nil
})
if err != nil {
fbLogger.Critical(ctx, "Critical error in main", "error", err)
}
}