こんにちは。Tomoyuki(@tomoyuki65)です。
以前にGo言語(Golang)のログ出力において「log/slog」が使えるのをご紹介しましたが、ログ出力時に合わせてソース情報も出力させることが可能です。
この記事では、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でカスタムロガーを設定する際はぜひ参考にしてみて下さい。

 
  
  
  
  

コメント