go directiveがGo 1.27以降のモジュールでは、トレースバックのgoroutineヘッダー行にruntime/pprofのゴルーチンラベルが既定で表示されるようになった
Runtime
概要
go.mod の go directiveでGo 1.27以降を指定するモジュールでは、パニック時などに出力される
トレースバックの goroutine ヘッダー行に、runtime/pprof
で設定したゴルーチンラベルが既定で含まれるようになった。この挙動は、Go 1.26で追加された
GODEBUG 設定 tracebacklabels=0 を指定することで無効化できる。
このオプトアウトは、ゴルーチンラベルに機微な情報が含まれてトレースバックに漏れてしまう
ケースに備え、今後も恒久的に維持される見込みである。
使用例
Before
package main
import (
"context"
"fmt"
"runtime"
"runtime/pprof"
)
func main() {
ctx := pprof.WithLabels(context.Background(), pprof.Labels("request_id", "abc123"))
pprof.SetGoroutineLabels(ctx)
buf := make([]byte, 1<<12)
n := runtime.Stack(buf, false)
fmt.Print(string(buf[:n]))
}
// go directiveがGo 1.27未満、またはGODEBUG=tracebacklabels=0の場合のヘッダー行:
// goroutine 1 [running]:
After
package main
import (
"context"
"fmt"
"runtime"
"runtime/pprof"
)
func main() {
ctx := pprof.WithLabels(context.Background(), pprof.Labels("request_id", "abc123"))
pprof.SetGoroutineLabels(ctx)
buf := make([]byte, 1<<12)
n := runtime.Stack(buf, false)
fmt.Print(string(buf[:n]))
}
// go directiveがGo 1.27以降の場合(既定)のヘッダー行:
// goroutine 1 [running] {request_id: abc123}:
移行時の注意
- ラベルのキー・値は、英数字・
.・/・_以外の文字を含む場合や、制御文字・改行を含む場合は
ダブルクォートで囲まれエスケープされた上で出力される。 - パニックログやトレースバックを機械的にパース・照合しているツールがある場合、ヘッダー行の末尾に
{key: value, ...}が挿入されることで既存のパース処理が壊れる可能性がある。影響がある場合は
GODEBUG=tracebacklabels=0で従来どおりの出力に固定できる。 - ゴルーチンラベルにトークンや個人情報など機微な情報を設定している場合、それらがトレースバックに
そのまま出力される点に注意が必要である。
実装解説
ヘッダー行への埋め込みは
runtime/traceback.go
の goroutineheader 相当の処理で行われており、gp.labels != nil && debug.tracebacklabels.Load() == 1
の場合に、ゴルーチンの label.Set をキーでソートした状態で {key: value, ...} の形式で
] の直後・: の直前に出力する。
tracebacklabels の既定値は
runtime1.go
の dbgvars テーブルで def: 1 として定義されており、
internal/godebugs/table.go
側で Changed: 27, Old: "0" と登録されている。この Changed/Old の組が、go directiveで
Go 1.27以降を指定したモジュールでは既定値 1(ラベル表示あり)を使い、それより前のモジュールでは
Old の値 0(ラベル非表示、従来動作)にフォールバックする仕組みを担っている。なお同エントリは
Opaque: true が指定されており、これはシグナルハンドラ内で生成されるトレースバックから
sync.Once を使う非デフォルト値計測(IncNonDefault)を呼び出せないための措置である。