Open7

s-logについて

zonozono

slogは構造化されたログシステム

  1. メッセージの内容
  2. ログのレベル
  3. キー&バリューのペアで構成できる

ログ設定例

package main

import "log/slog"

func main() {
	slog.Info("hello", "count", 3)
}

出力結果

2009/11/10 23:00:00 INFO hello count=3

https://go.dev/play/p/ythJ4_7aNDh

zonozono

テキスト形式で標準エラーに書き込む TextHandler をNewして作成

ログ構成をmsgやKey&Valueを含めて、視覚的に見やすくしている感じかな?

設定例

package main

import (
	"log/slog"
	"os"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
	logger.Info("hello", "count", 3)
    logger.Error("error")
}

出力結果

time=2009-11-10T23:00:00.000Z level=INFO msg=hello count=3
time=2009-11-10T23:00:00.000Z level=ERROR msg=""

https://go.dev/play/p/R8C_2qgxhh5

zonozono

JSON形式で出力

設定例

package main

import (
	"log/slog"
	"os"
)

func main() {
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
	logger.Info("hello", "count", 3)
}

出力結果

{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"hello","count":3}

これおもろい!Cloud Loggingとかでいい感じに出せるというやつか
https://go.dev/play/p/bzWFmol76f2

zonozono

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

設定例

package main

import (
	"log/slog"
	"os"
)

func main() {
	// カスタムロガーをNewして作成
	logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelDebug,
	}))

	// デフォルトロガーとして設定
	slog.SetDefault(logger)

	// これ以降、slog.Info()などはこのカスタムロガーが適用される
	slog.Info("アプリケーション開始", "version", "1.0.0")
	slog.Error("エラーが発生しました", "error", "connection timeout")
}

出力結果

{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"アプリケーション開始","version":"1.0.0"}
{"time":"2009-11-10T23:00:00Z","level":"ERROR","msg":"エラーが発生しました","error":"connection timeout"}

https://go.dev/play/p/9FSRXHUwxVB

zonozono

Withで設定したKey&Valueを含めるようにする

必ず共通で設定したい値を共通設定できるやつ

設定例

package main

import (
	"log/slog"
	"os"
)

func main() {
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

	logger.Info("通常のログ")

	// Withでlogger2はurlを含む
	logger2 := logger.With("url", "/api/users")

	logger2.Info("リクエスト開始")
	logger2.Error("エラーが発生", "error", "connection timeout")
	logger2.Info("処理完了", "status", "success", "duration", "100ms")
}

出力結果

{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"通常のログ"}
{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"リクエスト開始","url":"/api/users"}
{"time":"2009-11-10T23:00:00Z","level":"ERROR","msg":"エラーが発生","url":"/api/users","error":"connection timeout"}
{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"処理完了","url":"/api/users","status":"success","duration":"100ms"}

https://go.dev/play/p/v36-fVh8ysP

zonozono

Context

これも結構使うよね
Contextを含めることができる
トップレベルでは不要だけど、コンテキストを含めたい場面が多いと思う

設定例

package main

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

func main() {
	// JSONハンドラーでロガーを作成
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
	slog.SetDefault(logger)

	// 通常のコンテキスト
	ctx := context.Background()

	// コンテキストに値を追加
	ctx = context.WithValue(ctx, "user_id", "12345")
	ctx = context.WithValue(ctx, "request_id", "req-abc123")

	// InfoContextを使用してログ出力
	slog.InfoContext(ctx, "hello", "count", 3)

	// 比較用:通常のInfo
	slog.Info("hello without context", "count", 3)

	// 他のレベルでもContextが使える
	slog.ErrorContext(ctx, "error occurred", "error", "database timeout")
	slog.DebugContext(ctx, "debug info", "step", "validation")
}

出力例

{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"hello","count":3}
{"time":"2009-11-10T23:00:00Z","level":"INFO","msg":"hello without context","count":3}
{"time":"2009-11-10T23:00:00Z","level":"ERROR","msg":"error occurred","error":"database timeout"}

https://go.dev/play/p/otQG_FiaBdg

zonozono

Wrap

Printfのようにできるやーつ

設定例

package main

import (
	"fmt"
	"log/slog"
	"os"
)

func Infof(logger *slog.Logger, format string, args ...any) {
	logger.Info(fmt.Sprintf(format, args...))
}

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
	Infof(logger, "Hello %s, you have %d messages", "Taro", 5)
}

出力例

time=2009-11-10T23:00:00.000Z level=INFO msg="Hello Taro, you have 5 messages"

https://go.dev/play/p/EvodGP2MnZH