golog

package module
v0.0.0-...-1a08258 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: MIT Imports: 13 Imported by: 0

README

golog

⚡ 高性能でカラフルな Go ログハンドラー - log/slog をベースにした視認性とパフォーマンスに優れたログ出力

📸 表示例

gologを使うと、ログがこのように表示されます:

[2024-01-15 10:30:45.123] [DEBUG] msg="Application initializing" module="main"
[2024-01-15 10:30:45.456] [ INFO] msg="Server started" port=8080 env="production"
[2024-01-15 10:30:46.789] [ WARN] msg="High memory usage" usage_percent=85.3
[2024-01-15 10:30:47.012] [ERROR] msg="Database connection failed" error="timeout after 30s"

カラー出力有効時(ターミナル上):

  • 🔵 DEBUG - シアン色
  • 🟢 INFO - 緑色
  • 🟡 WARN - 黄色
  • 🔴 ERROR - 赤色

構造化ログ、属性、グループもサポート:

[2024-01-15 10:30:45.123] [ INFO] msg="User action" user.id=12345 user.name="alice" action="login" ip="192.168.1.1"
[2024-01-15 10:30:45.456] [ INFO] msg="API request" http.method="POST" http.path="/api/users" http.status=201 duration_ms=42

✨ 特徴

  • 🎨 カラー出力 - ログレベルごとの色分けで視認性向上
  • 高性能 - バッファプール、事前フォーマット、最適化された時刻処理
  • 📝 構造化ログ - 属性、グループによる構造化されたログ出力
  • 🔧 柔軟な設定 - ログレベル、時刻フォーマット、属性変換など
  • 🔍 デバッグ支援 - ソースファイル・行番号の自動追加
  • 🛡️ スレッドセーフ - 並行処理に完全対応
  • 🎯 標準準拠 - log/slog の Handler インターフェースを完全実装

📦 インストール

go get github.com/f0reth/golog

🚀 クイックスタート

最もシンプルな使い方
package main

import (
    "log/slog"
    "os"

    "github.com/f0reth/golog"
)

func main() {
    // ハンドラーを作成
    handler := golog.NewHandler(os.Stdout, &golog.Options{
        Level:     slog.LevelInfo,
        UseColors: true,
    })

    // ロガーを作成
    logger := slog.New(handler)

    // ログを出力
    logger.Info("Hello, golog!", "version", "1.0.0")
}

出力:

[2024-01-15 10:30:45.123] [ INFO] msg="Hello, golog!" version="1.0.0"

📖 使い方

基本的なログ出力
logger := slog.New(golog.NewHandler(os.Stdout, &golog.Options{
    Level:     slog.LevelDebug,
    UseColors: true,
}))

logger.Debug("詳細なデバッグ情報", "variable", someValue)
logger.Info("通常の情報", "count", 42)
logger.Warn("警告メッセージ", "threshold", 80)
logger.Error("エラー発生", "error", err)
構造化ログと属性

属性を追加して、ログに構造化されたデータを含めることができます:

// 複数の属性を追加
logger.Info("ユーザーがログインしました",
    "user_id", 12345,
    "username", "alice",
    "ip", "192.168.1.1",
    "login_time", time.Now(),
)

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="ユーザーがログインしました" user_id=12345 username="alice" ip="192.168.1.1" login_time="2024-01-15T10:30:45.123Z"
コンテキスト付きロガー

共通の属性を持つロガーを作成できます:

// ユーザーIDを常に含むロガー
userLogger := logger.With("user_id", 12345, "session_id", "abc123")

// このロガーを使うと、すべてのログに user_id と session_id が含まれる
userLogger.Info("プロフィール更新")
userLogger.Info("ファイルアップロード", "filename", "avatar.jpg")

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="プロフィール更新" user_id=12345 session_id="abc123"
// [2024-01-15 10:30:45.456] [ INFO] msg="ファイルアップロード" user_id=12345 session_id="abc123" filename="avatar.jpg"
グループ化

関連する属性をグループ化できます:

// データベース関連のログをグループ化
logger.WithGroup("database").Info("クエリ実行",
    "table", "users",
    "duration_ms", 42,
    "rows_affected", 1,
)

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="クエリ実行" database.table="users" database.duration_ms=42 database.rows_affected=1

// ネストされたグループ
logger.WithGroup("server").WithGroup("http").Info("リクエスト受信",
    "method", "GET",
    "path", "/api/users",
    "status", 200,
)

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="リクエスト受信" server.http.method="GET" server.http.path="/api/users" server.http.status=200
ソースコードの場所を表示

デバッグ時にソースファイルと行番号を表示できます:

handler := golog.NewHandler(os.Stdout, &golog.Options{
    Level:     slog.LevelDebug,
    UseColors: true,
    AddSource: true,  // ソース情報を追加
})

logger := slog.New(handler)
logger.Debug("デバッグメッセージ")

// 出力:
// [2024-01-15 10:30:45.123] [DEBUG] msg="デバッグメッセージ" source="main.go:42"
時刻フォーマットのカスタマイズ
import "time"

// RFC3339形式
handler := golog.NewHandler(os.Stdout, &golog.Options{
    TimeFormat: time.RFC3339,
})
// 出力: [2024-01-15T10:30:45Z] [ INFO] msg="test"

// 日付のみ
handler = golog.NewHandler(os.Stdout, &golog.Options{
    TimeFormat: "2006-01-02",
})
// 出力: [2024-01-15] [ INFO] msg="test"

// カスタムフォーマット
handler = golog.NewHandler(os.Stdout, &golog.Options{
    TimeFormat: "15:04:05.000",  // 時刻のみ、ミリ秒付き
})
// 出力: [10:30:45.123] [ INFO] msg="test"

// デフォルト(省略時): "2006-01-02 15:04:05.000"
属性の変換(ReplaceAttr)

ログ出力時に属性を変換できます:

handler := golog.NewHandler(os.Stdout, &golog.Options{
    Level:     slog.LevelInfo,
    UseColors: true,
    ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
        // パスワードをマスキング
        if a.Key == "password" {
            return slog.String("password", "***REDACTED***")
        }

        // 内部属性を削除
        if a.Key == "internal_id" {
            return slog.Attr{} // 空のキー = 無視
        }

        // 時刻を固定値に(テスト用)
        if a.Key == slog.TimeKey {
            return slog.Time(slog.TimeKey, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))
        }

        return a
    },
})

logger := slog.New(handler)
logger.Info("ログイン試行", "username", "alice", "password", "secret123", "internal_id", "xyz")

// 出力:
// [2000-01-01 00:00:00.000] [ INFO] msg="ログイン試行" username="alice" password="***REDACTED***"
// ※ internal_id は出力されない、時刻も変更されている
カスタム型のフォーマット
slog.LogValuer(標準インターフェース)
// 機密情報のマスキング
type Password string

func (p Password) LogValue() slog.Value {
    return slog.StringValue("[REDACTED]")
}

// カスタムフォーマット
type UserID int

func (u UserID) LogValue() slog.Value {
    return slog.StringValue(fmt.Sprintf("user_%d", u))
}

logger.Info("ユーザー認証",
    "user_id", UserID(12345),
    "password", Password("secret"),
)

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="ユーザー認証" user_id="user_12345" password="[REDACTED]"
LogFormatter(golog独自インターフェース)
type User struct {
    ID   int
    Name string
}

func (u User) FormatForLog() (string, error) {
    return fmt.Sprintf(`"%s(id:%d)"`, u.Name, u.ID), nil
}

logger.Info("ユーザー作成", "user", User{ID: 123, Name: "Alice"})

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="ユーザー作成" user="Alice(id:123)"

⚙️ オプション一覧

オプション デフォルト 説明
Level slog.Leveler slog.LevelInfo 最小ログレベル
UseColors bool false カラー出力の有効化
TimeFormat string "2006-01-02 15:04:05.000" 時刻のフォーマット
AddSource bool false ソースファイル・行番号の追加
ReplaceAttr func([]string, slog.Attr) slog.Attr nil 属性の変換関数

🎯 実用例

Webサーバーのログ
handler := golog.NewHandler(os.Stdout, &golog.Options{
    Level:     slog.LevelInfo,
    UseColors: true,
})

logger := slog.New(handler).WithGroup("http")

http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()

    // リクエスト処理...

    logger.Info("リクエスト完了",
        "method", r.Method,
        "path", r.URL.Path,
        "status", 200,
        "duration_ms", time.Since(start).Milliseconds(),
        "ip", r.RemoteAddr,
    )
})

// 出力:
// [2024-01-15 10:30:45.123] [ INFO] msg="リクエスト完了" http.method="GET" http.path="/api/users" http.status=200 http.duration_ms=42 http.ip="192.168.1.1"
エラーハンドリング
logger := slog.New(golog.NewHandler(os.Stderr, &golog.Options{
    Level:     slog.LevelError,
    UseColors: true,
    AddSource: true,
}))

if err := doSomething(); err != nil {
    logger.Error("操作に失敗しました",
        "error", err,
        "operation", "database_update",
        "retry_count", 3,
    )
}

// 出力:
// [2024-01-15 10:30:45.123] [ERROR] msg="操作に失敗しました" source="main.go:123" error="connection timeout" operation="database_update" retry_count=3
環境別の設定
func setupLogger(env string) *slog.Logger {
    opts := &golog.Options{
        UseColors: true,
    }

    switch env {
    case "production":
        opts.Level = slog.LevelWarn  // 本番は WARN 以上のみ
        opts.AddSource = false
    case "development":
        opts.Level = slog.LevelDebug  // 開発は DEBUG から
        opts.AddSource = true
    default:
        opts.Level = slog.LevelInfo
    }

    handler := golog.NewHandler(os.Stdout, opts)
    return slog.New(handler)
}

⚡ パフォーマンス

gologは高性能を実現するために以下の最適化を実装しています:

最適化技術
  1. バッファプール - sync.Poolによるバッファの再利用でアロケーションを最小化
  2. 事前フォーマット - WithAttrsで追加された属性を事前にフォーマット
  3. 最適化された時刻処理 - よく使われるフォーマットは専用の高速実装(約2倍高速)
  4. ゼロアロケーション設計 - slog.Value.Kind()による型スイッチでインターフェースボクシングを回避、strconv.AppendQuoteによるバッファ直接書き込み
  5. 高速パス分岐 - ReplaceAttr未使用時は不要なslog.Attr生成をスキップ
  6. ミューテックス共有 - ハンドラーのクローン間でミューテックスを共有
ベンチマーク結果

標準ライブラリの slog.JSONHandler との比較(同一条件):

golog:
BenchmarkHandle-24              3084242    372.4 ns/op     8 B/op    1 allocs/op
BenchmarkHandleConcurrent-24    4954995    251.5 ns/op   333 B/op    0 allocs/op

slog.JSONHandler:
BenchmarkSlog-24                2726260    440.0 ns/op     8 B/op    0 allocs/op
BenchmarkSlogConcurrent-24      4981743    247.3 ns/op   439 B/op    0 allocs/op

→ 単体処理で標準slogより約15%高速、並行処理でメモリ使用量24%削減

時刻フォーマットの最適化:

DefaultFormatOptimized-24       25648314    46.97 ns/op    0 B/op    0 allocs/op
DefaultFormatAppendFormat-24    13928206    86.04 ns/op    0 B/op    0 allocs/op

→ デフォルトフォーマットで約2倍高速化

🧪 テスト

# すべてのテストを実行
go test

# カバレッジ付き
go test -cover

# 詳細表示
go test -v

# race detector
go test -race

# ベンチマーク
go test -bench=. -benchmem

📝 ログフォーマット仕様

[時刻] [レベル] msg="メッセージ" key1="value1" key2=value2 ...
  • 時刻: 設定可能なフォーマット(デフォルト: 2006-01-02 15:04:05.000
  • レベル: DEBUG, INFO, WARN, ERROR(5文字幅で統一)
  • msg: ログメッセージ
  • 属性: key=value形式、文字列値はダブルクォートで囲まれる
キーのエスケープ

特殊文字を含むキーは自動的にエスケープされます:

logger.Info("test", "my key", "value")        // "my key"="value" (スペース)
logger.Info("test", "key=name", "value")      // "key=name"="value" (イコール)
logger.Info("test", `key"name`, "value")      // "key\"name"="value" (クォート)

🤝 貢献

バグ報告や機能リクエストは、GitHubのIssueでお願いします。

📄 ライセンス

このプロジェクトは Go 標準ライブラリの log/slog の設計パターンを参考にしています。

🔗 関連リンク

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler は指定されたフォーマットでログを出力するハンドラー

func NewHandler

func NewHandler(w io.Writer, opts *Options) *Handler

NewHandler は新しいカスタムハンドラーを作成します

func (*Handler) Enabled

func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool

Enabled はログレベルが有効かどうかを判断します

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, r slog.Record) error

Handle はログレコードを処理します

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs は新しい属性を持つハンドラーを返します

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup は新しいグループを持つハンドラーを返します

type LogFormatter

type LogFormatter interface {
	FormatForLog() (string, error)
}

LogFormatter はログ出力のためのカスタムフォーマットを提供するインターフェース

type Options

type Options struct {
	Level       slog.Leveler
	UseColors   bool
	TimeFormat  string // 空の場合は "2006-01-02 15:04:05.000" を使用
	AddSource   bool
	ReplaceAttr func(groups []string, a slog.Attr) slog.Attr
}

Options はカスタムハンドラーのオプション

Directories

Path Synopsis
internal
buffer
Package buffer provides a pool-allocated byte buffer.
Package buffer provides a pool-allocated byte buffer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL