関連記事
Goメモ-477 (slogメモ-01)(基本的な使い方) - いろいろ備忘録日記
Goメモ-478 (slogメモ-02)(構造化ログの出力) - いろいろ備忘録日記
Goメモ-479 (slogメモ-03)(デフォルトロガー) - いろいろ備忘録日記
Goメモ-480 (slogメモ-04)(従来のlogパッケージとの連携) - いろいろ備忘録日記
Goメモ-482 (slogメモ-05)(テキスト形式のログ) - いろいろ備忘録日記
Goメモ-483 (slogメモ-06)(JSON形式のログ) - いろいろ備忘録日記
Goメモ-484 (slogメモ-07)(動的にログレベルを変更) - いろいろ備忘録日記
Goメモ-485 (slogメモ-08)(グループ (1)) - いろいろ備忘録日記
Goメモ-486 (slogメモ-09)(グループ (2)) - いろいろ備忘録日記
Goメモ-487 (slogメモ-10)(機密情報などのマスキング) - いろいろ備忘録日記
Goメモ-488 (slogメモ-11)(カスタムログレベル) - いろいろ備忘録日記
Goメモ-490 (slogメモ-12)(context.Contextとの連携) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
今更ながら、Go 1.21で導入された log/slog
を使ってみたりしています。
少しづつメモしていきます。今回はio.Writerとの連携について。
slogでは、ハンドラに io.Writer を渡して出力してもらうことになっています。
io.Writerが渡せるので、好きに調整できますね。
以下のサンプルでは、os.Stdout
と lumberjack.Logger
を io.MultiWriter
で包んでハンドラに指定しています。
サンプル
main.go
package main import ( "context" "errors" "io" "log" "log/slog" "os" "time" "gopkg.in/natefinch/lumberjack.v2" ) const ( MainTimeout = time.Second ProcTimeout = 500 * time.Millisecond ) var ( ErrMainTooSlow = errors.New("(MAIN) TOO SLOW") ErrProcTooSlow = errors.New("(PROC) TOO SLOW") ) func init() { log.SetFlags(0) } func main() { var ( rootCtx = context.Background() mainCtx, mainCxl = context.WithTimeoutCause(rootCtx, MainTimeout, ErrMainTooSlow) procCtx = run(mainCtx) err error ) defer mainCxl() select { case <-mainCtx.Done(): err = context.Cause(mainCtx) case <-procCtx.Done(): if err = context.Cause(procCtx); errors.Is(err, context.Canceled) { err = nil } } if err != nil { log.Fatal(err) } } func run(pCtx context.Context) context.Context { var ( ctx, cxl = context.WithCancelCause(pCtx) ) go func() { cxl(proc(ctx)) }() go func() { <-time.After(ProcTimeout) cxl(ErrProcTooSlow) }() return ctx } func proc(_ context.Context) error { // // slogでは、出力先をハンドラにて指定できる。 // 出力先には、 io.Writer を指定出来るので好きに調整出来る。 // // ここに gopkg.in/natefinch/lumberjack.v2 などを // 設定することにより、ローリングも可能になる。 // // 本サンプルでは、 os.Stdout と lumberjack.Logger を // io.MultiWriter で包んで slog.Handler に指定している。 // var ( level = &slog.LevelVar{} opt = &slog.HandlerOptions{ Level: level, ReplaceAttr: replaceAttr, } writer, closeFn = newWriter() handler = slog.NewJSONHandler(writer, opt) rootLogger = slog.New(handler) logger *slog.Logger ) defer closeFn() logger = rootLogger.With("loop-count", 10) for i := range 10 { // 奇数番目のときだけログに出るように小細工 level.Set(slog.LevelDebug) if i%2 == 0 { level.Set(slog.LevelInfo) } logger.Debug("helloworld", "i", i) } return nil } func newWriter() (io.Writer, func()) { var ( writer1 = os.Stdout writer2 = &lumberjack.Logger{ Filename: "/tmp/try-golang/slog-example/app.log", MaxSize: 1, MaxBackups: 3, MaxAge: 7, Compress: false, } writer = io.MultiWriter(writer1, writer2) ) return writer, func() { writer2.Close() } } func replaceAttr(g []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { return slog.Attr{} } return a }
実行すると以下のように出力されます。
Taskfile.yml
# https://taskfile.dev version: '3' tasks: default: cmds: - task: run run: cmds: - go run main.go - defer: rm -rf /tmp/try-golang - ls -1 /tmp/try-golang/slog-example - cat /tmp/try-golang/slog-example/*.log
shell
$ task task: [run] go run main.go {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":1} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":3} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":5} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":7} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":9} task: [run] ls -1 /tmp/try-golang/slog-example app.log task: [run] cat /tmp/try-golang/slog-example/*.log {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":1} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":3} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":5} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":7} {"level":"DEBUG","msg":"helloworld","loop-count":10,"i":9} task: [run] rm -rf /tmp/try-golang
ちゃんと標準出力にも出力されて、ログファイルにも出力されてますね。
try-golang/examples/slog at main · devlights/try-golang · GitHub
参考情報
- Structured Logging with slog
- log/slog
- A Guide to Writing slog Handlers
- awesome-slog
- Goのslog使い方まとめ
- Go1.21で登場したlog/slogパッケージのパフォーマンスを徹底解説!!
- Go1.21 log/slogパッケージ超入門
- Go公式の構造化ロガー(として提案されている)slogを触ってみたメモ
- slog を触る(Group, Context)
- 構造化ログと実装 -Goのslogによる実践-
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。