Upgrade to Pro — share decks privately, control downloads, hide ads and more …

slog登場に伴うloggerの取り回し手法の見直し / kamakura.go #6

Arthur
May 17, 2024

slog登場に伴うloggerの取り回し手法の見直し / kamakura.go #6

Arthur

May 17, 2024
Tweet

More Decks by Arthur

Other Decks in Programming

Transcript

  1. 構造体のフィールドにloggerを入れる 17 type UserService struct { logger Logger } func

    NewUserService() { logger := log.WithFields(log.Fields{“user-id”: 1}) return &UserService{logger: logger} } (u *UserService) Login() { u.logger.Info(“success”) }
  2. 構造体のフィールドにloggerを入れる 18 Pros • その構造体がリクエストごと生成されるならば、リクエストご とのloggerを置く場所として利用できる Cons • その構造体のレシーバでなければloggerを利用できない •

    ジェネリクスを使いたい時など、どうしてもレシーバにできな いこともある • 超える際には明示的に別の方法で引き渡す必要がある ◦ 引数にloggerを入れるのと同じ苦しみが待っている
  3. contextにloggerを入れる 19 package mylog import “context” type key struct{} //

    loggerをcontextに格納 func NewContext(ctx context.Context, logger Logger) context.Context { return context.WithValue(ctx, key{}, logger) } // contextからloggerを取り出す func FromContext(ctx context.Context) Logger { return ctx.Value(key{}).(Logger) }
  4. contextにloggerを入れる 20 func A() { logger := log.WithFields(log.Fields{“user-id”: 1}) ctx

    := mylog.NewContext(context.Background(), logger) B(ctx) } func B(ctx context.Context) { logger := mylog.FromContext(ctx) logger.Info(“Hello, world.”) }
  5. 実装例 29 var keys = []string{“user_id”, “request_id”} type MyLogHandler struct

    { slog.Handler } func (h *MyLogHandler) Handle(ctx context.Context, r slog.Record) error { for _, key := range keys { if v := fromContext(ctx, key); v != nil { r.AddAttrs(slog.Attr{Key: string(key), Value: slog.AnyValue(v)}) } } return h.Handler.Handle(ctx, r) }
  6. 実装例 30 func main() { logger := slog.New(&MyLogHandler{slog.NewJSONHandler(os.Stderr, nil)}) slog.SetDefault(logger)

    A() } func A() { ctx := context.WithValue(context.Background(), "user_id", 1) ctx = context.WithValue(ctx, "request_id", "000") B(ctx) } func B(ctx context.Context) { slog.InfoContext(ctx, "Hello, world!") }