LoginSignup
9
0

More than 1 year has passed since last update.

Goでスタックトレースを実装

Last updated at Posted at 2022-12-08

スタックトレースとは

実行中のプログラムがエラーを起こした際に、その直前まで実行していた関数やメソッドの履歴を表示すること。

インタプリタ型言語の実行環境では準備されていて、デバッグ時にエラーを出した際にはスタックトレースが見られることが多い。

サーバーサイドのデバッグにはこのスタックトレースがあるかないかで作業効率に天と地ほどの差を生む(と考えている)ので
この記事ではGoでのスタックトレースの実装方法を紹介する。

Goでの実装

環境: go1.17.10

使用するパッケージ
https://pkg.go.dev/runtime

main.go
package main

import (
    "runtime"
    "fmt"
)

func showStackTrace() {
    i := 0
    for {
        pt, file, line, ok := runtime.Caller(i)
        if !ok {
            break
        }
        funcName := runtime.FuncForPC(pt).Name()
        fmt.Printf("file=%s, line=%d, func=%v\n", file, line, funcName)
        i += 1
    }
}

解説

runtime.Caller(i)とすると4つの返り値を取得できる。
返り値は順にプログラムのカウンター、ファイル名、行数となる。最後のokはboolean型で情報が取得できなかった時にはfalseとなる。

そのためokがfalseになったときにループ処理を抜けるようにbreakするようにし、プログラムカウンターをインクリメントしていくとスタックトレースに必要な情報を取得できる。

また、runtime.FuncForPC(pt).Name()でプログラムカウンターに含まれる関数名を取得できる。

最後にfmt.Printf や log.Printfで任意のフォーマットでロギングする。

実際の使い方

スタックトレースはエラーの時に表示したい場合が多い。
そのためエラーハンドリングの際に共通してスタックトレースを表示する実装例を示す。

main.go
package main

import (
	"errors"
	"fmt"
	"runtime"
)

func main() {
	if err := doSomething(); err != nil {
		handleError(err)
	}
}

func doSomething() error {
	fmt.Println("Do something")
	return errors.New("An error occurred")
}

func handleError(err error) {
    fmt.Println(err.Error())
	i := 0
    for {
        pt, file, line, ok := runtime.Caller(i)
        if !ok {
            break
        }
        funcName := runtime.FuncForPC(pt).Name()
        fmt.Printf("file=%s, line=%d, func=%v\n", file, line, funcName)
        i += 1
    }
}

上記の例ではdoSomething()でエラーを返すようにしhandleError(err)の引数にerrorを渡すようにした。
handleError(err)ではerrorの内容とスタックトレースを出力するようにした。

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0