PR

Go言語(Golang)のslogでソース情報も合わせて出力する方法

2. 基礎

こんにちは。Tomoyuki(@tomoyuki65)です。

以前にGo言語(Golang)のログ出力において「log/slog」が使えるのをご紹介しましたが、ログ出力時に合わせてソース情報も出力させることが可能です。

この記事では、slogでソース情報も合わせて出力する方法についてご紹介します。

 

関連記事

Go言語(Golang)のログ出力ではslogが使える!コンテキストの引き回しも忘れずに。
こんにちは。Tomoyuki(@tomoyuki65)です。Go言語(Golang)でログ出力をする際にはfmt.Printlnなどを使うことがありますが、Goのv1.21からは標準ライブラリとして"log/slog"が使えます。この記事で...

 

Go言語(Golang)のslogでソース情報も合わせて出力する方法

まずslogのInfoメソッドで文字列「Hello World !!」を出力する例は以下の通りです。

package main

import (
    "log/slog"
)

func main() {
    slog.Info("Hello World !!")
}

 

実行結果

2025/10/25 11:02:02 INFO Hello World !!

 

次にソース情報も合わせて出力させますが、それにはハンドラーオプションでソース情報追加を有効化「AddSource: true」(goのバージョン1.21以上から使えるようです)し、デフォルトロガーの設定を変更してから実行します。

package main

import (
    "log/slog"
    "os"
)

func main() {
    // デフォルトロガーの設定変更
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        // ソース情報追加を有効化
        AddSource: true,
    }))
    slog.SetDefault(logger)

    slog.Info("Hello World !!")
}

※設定変更でログの出力形式が変わるのは仕様ですが、今回は詳細情報が出力されるJSON形式「slog.NewJSONHandler()」を使っています。

 

実行結果

{"time":"2025-10-25T11:16:13.446338925Z","level":"INFO","source":{"function":"main.main","file":"/go/src/main.go","line":35},"msg":"Hello World !!"}

※ソース情報は「”source”:{“function”:”main.main”,”file”:”/go/src/main.go”,”line”:35}」の部分です。

 

カスタムロガーを設定する場合

slogではカスタムロガーを設定することができますが、その際にソース情報も出力したい場合は、ハンドラー設定では「runtime」も使って設定し、かつaddSourceフラグで切り替えられるようにカスタムロガーを定義して実現可能です。

package main

import (
    "context"
    "log/slog"
    "os"
    "runtime"
)

// カスタムロガー用のハンドラー設定
type SlogHandler struct {
    slog.Handler
}

func (h *SlogHandler) Handle(ctx context.Context, r slog.Record) error {
    // rをコピー
    newRecord := r.Clone()

    // runtimeからプログラムカウンターを取得して上書き
    // runtime.Caller(n)の数値は適切な値を設定する
    pc, _, _, ok := runtime.Caller(4)
    if ok {
        newRecord.PC = pc
    }

    return h.Handler.Handle(ctx, newRecord)
}

var slogHandler = &SlogHandler{
    slog.NewJSONHandler(os.Stdout, nil),
}

var slogHandlerAddSource = &SlogHandler{
    slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        AddSource: true,
    }),
}

// slogの定義
var logger = slog.New(slogHandler)
var loggerAddSource = slog.New(slogHandlerAddSource)

// カスタムロガー用のインターフェース設定
type Logger interface {
    Info(addSource bool, ctx context.Context, message string)
}

// カスタムロガーの定義
type customLogger struct{}

func NewLogger() Logger {
    return &customLogger{}
}

func (cl *customLogger) Info(addSource bool, ctx context.Context, message string) {
    if addSource {
        loggerAddSource.InfoContext(ctx, message)
    } else {
        logger.InfoContext(ctx, message)
    }
}

func main() {
    // ロガー設定
    logger := NewLogger()

    // コンテキスト設定
    ctx := context.Background()

    // ログ出力
    logger.Info(false, ctx, "Hello World !!")

    // ログ出力(ソース情報有り)
    logger.Info(true, ctx, "Hello World !! Add Source !!")
}

※runtime.Caller(4)の数値については、「コールスタックを何階層遡るか」を指定する値になっています。

 

実行結果

{"time":"2025-10-25T14:07:40.4669005Z","level":"INFO","msg":"Hello World !!"}
{"time":"2025-10-25T14:07:40.466957Z","level":"INFO","source":{"function":"main.main","file":"/go/src/main.go","line":74},"msg":"Hello World !! Add Source !!"}

 

スポンサーリンク

最後に

今回はslogでソース情報も合わせて出力する方法についてご紹介しました。

実務における障害対応などではロガーの設定はかなり重要になるので、slogでカスタムロガーを設定する際はぜひ参考にしてみて下さい。

 

コメント

タイトルとURLをコピーしました