Slide 1

Slide 1 text

スタックトレース始めてみた 2024/05/15 Monthly LT Go プリズムパートナーカンパニー kuro

Slide 2

Slide 2 text

みなさんはGoのスタックトレース使って ますか?

Slide 3

Slide 3 text

なぜ欲しいのか •DataDogのError Trackingを使いたい!⇦比重大 •エラーデバッグをもっと簡単にしたい。

Slide 4

Slide 4 text

DataDog ErrorTrackingとは 1. エラーをいい感じにグループ化してくれる。 2. 通知もエラーごとにいい感じに制御してくれる →以下の2種類のモニターを使える。 Count: エラーのグループごとに一定の値を超       えたら通知してくれる。 
     New Issue: 新しいエラーが発生したら通知し         てくれる。
 →ある一定の値を超えるエラーが出た場合のみ対 応したいみたいなニーズにあう。 参照 https://www.datadoghq.com/ja/product/error-tracking/

Slide 5

Slide 5 text

DataDog ErrorTracking導入 当初 ErrorTrackingをONにするだけか😍 実際 あれ、ONにして待てど暮らせど何も出てこない。。。 👇 ログの構造を変える必要がある。 参照:https://docs.datadoghq.com/ja/logs/error_tracking/backend/?tab=serilog 属性 説明 error.stack 実際のスタックトレース error.message スタックトレースに含まれるエラーメッセージ error.kind エラーのタイプまたは「種類」 ("Exception" や "OSError" など)

Slide 6

Slide 6 text

ほな、スタックトレース入れてみますか

Slide 7

Slide 7 text

懸念点・考慮すべき点 - パフォーマンスが下がる可能性あるかも。 - 参照:はてなブログ Goのerrorがスタックトレースを含まない理由 - やっぱやめようとなったときに剥がしやすい仕組みにしたい。 - エラーをラップする部分を忘れないようにしたい。 - 大きな修正はしたくない。 - slog(https://pkg.go.dev/golang.org/x/exp/slog)との相性も考えたい。

Slide 8

Slide 8 text

比較・検討 1. pkg errors or xerrorsをそのまま使う。→メンテされてないのが🥺 a. https://pkg.go.dev/github.com/pkg/errors b. https://pkg.go.dev/golang.org/x/xerrors 2. その他の色々OSSを使う→要件に対してtoo muchなのと大体全てのエラーをラップするのが必要 a. https://github.com/juju/errors b. https://github.com/go-errors/errors c. https://github.com/morikuni/failure d. https://github.com/hashicorp/go-multierror e. https://github.com/rotisserie/eris f. https://github.com/cockroachdb/errors 3. 自前 a. pkg errors or xerrorsあたりの実装を引っ張ってくる b. debug.Stack(https://pkg.go.dev/runtime/debug#Stack)や、 runtime.Callers(https://pkg.go.dev/runtime#Callers)のメソッドを使う

Slide 9

Slide 9 text

runtimeパッケージでスタックトレース - func Stack() []byte - スタックトレースをバイト列にして返してくれる。 - func Callers(skip int, pc []uintptr) int + func CallersFrames(callers []uintptr) *Frames - Callersが返すプログラムカウンタをCallersFramesに渡すと関数/ファイル/行の情報を返してくれ る。 →ちょっとだけ*Framesの加工がめんどい

Slide 10

Slide 10 text

スタックトレースを作成 - runtime.Stackでスタックトレース を作成して、StackError構造体 を返す。

Slide 11

Slide 11 text

ラップする方法 - pkgsite/internal/derrors 参考に。 (https://cs.opensource.google/go/x/pkgsite/+/master:internal/derrors/derrors.go) - deferで、関数を抜ける時に WrapStackを呼び出す。

Slide 12

Slide 12 text

ラップする方法 - WrapStackでは、エラーが - StackError型の場合→errをwrap する。 - StackError型でない場合→errを 新しくStackError構造体に。 - ダブルポインタを使うことでdeferで関数 を抜ける際にエラーを直接更新する。 - ただ、名前付き戻り値にする必要あり。

Slide 13

Slide 13 text

ラップする方法 - 全てのエラーごとにWrapを追加せずに済む →関数ごとにdeferを入れてwrapするだけ。 →静的解析もしやすいのではないか(展望) - ただ、エラーを更新するために、名前付き戻り値に書き換えるのがちょっと大 変。。

Slide 14

Slide 14 text

slogとの組み合わせ - LogValuer(https://pkg.go.dev/golang.org/x/exp/slog#LogValuer)インターフェースを満たすようにしてロギングの際 にstack,message,kindを出力できるようにログを整形する。 - 以下のようにして、StackError型のエラーをslogで出力すると意図通りの構造化ログになる。

Slide 15

Slide 15 text

試している方法 - 自前エラーパッケージを追加する。 - 一部のコンポーネントでdefer+名前付き戻り値に置き換える。 - Datadog Error Trackingが役に立ちそうか検証中。

Slide 16

Slide 16 text

これから - DataDog Error Trackingが良さそうなら、エラーがよく出る部分からさらに追加して いく。 - スタックトレースを始めて、パフォーマンスがどう変わったのかDataDogで計測し たい。 - deferのWrap忘れを検知できるようなlinterを何かしら導入したい。

Slide 17

Slide 17 text

参考 - Error Tracking | Datadog - バックエンドエラーの追跡 - Datadog Error Tracking for Logsを使って日々のエラーログ監視を省力化する - Goでスタックトレースを扱う方法がややこしい件について - Speaker Deck - Goのerrorがスタックトレースを含まない理由 - methaneのブログ - pkgsite/internal/derrors を使ったGoのエラーラッピング - Goでスタイリッシュにエラーをラップする方法を学んだ - カミナシ エンジニアブログ

Slide 18

Slide 18 text

ご清聴ありがとうございました