Skip to content

Commit 32229a4

Browse files
authored
Merge pull request #867 from devlights/add-slog-example
2 parents e411b15 + 28b2c5e commit 32229a4

File tree

1 file changed

+44
-6
lines changed
  • examples/slog/15.custom-handler1

1 file changed

+44
-6
lines changed

examples/slog/15.custom-handler1/main.go

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,43 @@ import (
1111
)
1212

1313
type (
14+
// counterHandler は、内部でログ出力回数をカウントし、その内容を出力するカスタムハンドラです。
15+
// ログ出力自体は、作成時に指定した元ハンドラに任せます。
16+
//
17+
// slogでカスタムハンドラを作成する場合, `slog.Handler` インターフェースを満たす必要があります。
18+
// `slog.Handler` インターフェースには以下のメソッドが定義されています。(https://pkg.go.dev/log/slog@go1.23.2#Handler)
19+
//
20+
// - Enabled(context.Context, Level) bool
21+
// - Handle(context.Context, Record) error
22+
// - WithAttrs(attrs []Attr) Handler
23+
// - WithGroup(name string) Handler
24+
//
25+
// コアとなる処理は `slog.Handler.Handle` メソッドです。
26+
// `go doc` にある通り、独自実装する場合は以下のルールに従うべきであると記載されています。
27+
//
28+
// - If r.Time is the zero time, ignore the time.(r.Timeがゼロ時刻の場合は、時刻を無視する)
29+
// - If r.PC is zero, ignore it.(r.PCがゼロの場合、無視する)
30+
// - Attr's values should be resolved.(Attrの値は解決されるべき)
31+
// - If an Attr's key and value are both the zero value, ignore the Attr. This can be tested with attr.Equal(Attr{}).(Attrのkeyとvalueの両方がゼロの場合、そのAttrは無視される。これは attr.Equal(Attr{}) でテスト可能)
32+
// - If a group's key is empty, inline the group's Attrs.(グループのキーが空の場合、グループのAttrsをインライン化する)
33+
// - If a group has no Attrs (even if it has a non-empty key), ignore it.(グループにAttrがない場合は (空でないキーがある場合でも)、無視する)
34+
//
35+
// 詳細な解説については [A Guide to Writing slog Handlers](https://github.com/golang/example/blob/master/slog-handler-guide/README.md) を参照。
36+
// (日本語訳を添えてあるものを [slog-handler-guide-ja.md](./slog-handler-guide-ja.md) として置いています。)
37+
//
38+
// 現実的に、フルカスタムのハンドラをゼロから自前で作成することはほぼ無いと思います。
39+
// 基本は、元から存在している何かのハンドラ(slog.JSONHandlerなど)を元にして、その上に独自の処理を付け加えることになります。
40+
//
41+
// その場合、ログ出力の部分は元ハンドラに移譲する形で完了するが、実装時に以下の点には注意する必要がある。
42+
//
43+
// 1. `WithAttrs`と`WithGroup`メソッドを上書きでメソッド定義しておかないと、カスタムハンドラをハンドラとして生成したロガーにて `Logger.With()` または `Logger.WithGroup()` を使ってロガーを複製した場合に属性が引き継がれない状態が発生する。
44+
// 2. 内部で何らかの状態を保持する必要がある場合は、複数のゴルーチンにて同時に呼び出されることを考慮してロックする。
45+
// 3. 2の件に加えて、`slog.Logger.With()` または `slog.Logger.WithGroup()` が利用されることが想定される場合は値やミューテックスをポインタで持っておく。(これらのメソッドではハンドラをコピーするため)
46+
//
1447
counterHandler struct {
15-
slog.Handler
16-
count *int
17-
mu *sync.Mutex
48+
slog.Handler // 内部で利用する元ハンドラ
49+
count *int // カウンタ
50+
mu *sync.Mutex // ロック用
1851
}
1952
)
2053

@@ -30,9 +63,12 @@ func (me *counterHandler) Handle(ctx context.Context, r slog.Record) error {
3063
me.mu.Lock()
3164
defer me.mu.Unlock()
3265

66+
//
67+
// ミリ秒と呼び出し回数を出力
68+
//
3369
r.AddAttrs(slog.String("millis", time.Now().Format(".000")))
34-
r.AddAttrs(slog.Int("count", *me.count))
3570
*me.count++
71+
r.AddAttrs(slog.Int("count", *me.count))
3672

3773
return me.Handler.Handle(ctx, r)
3874
}
@@ -52,17 +88,19 @@ func main() {
5288
Level: level,
5389
ReplaceAttr: replaceAttr,
5490
}
55-
writer = os.Stdout
91+
writer = os.Stderr
5692
handler = newHandler(slog.NewTextHandler(writer, opt))
5793
logger = slog.New(handler)
5894

5995
wg sync.WaitGroup
6096
goroutineCount = runtime.NumCPU() * 2
6197
loopCount = 2
6298
)
63-
6499
logger.Info("Start", "NumCPU", runtime.NumCPU(), "goroutineCount", goroutineCount)
65100

101+
//
102+
// 複数のゴルーチンから並行してログ出力
103+
//
66104
wg.Add(goroutineCount)
67105
for i := range goroutineCount {
68106
go func(logger *slog.Logger) {

0 commit comments

Comments
 (0)