Golangプログラムで、呼び出したエラーの行番号を出力するにはどうすればよいですか?


94

私はGolangプログラムでエラーをスローしようとしましたlog.Fatalが、log.Fatal実行された行も出力しませんlog.Fatal。log.Fatalを呼び出した行番号にアクセスする方法はありませんか?つまり、エラーをスローするときに行番号を取得する方法はありますか?

私はこれをグーグル化しようとしていましたが、どうしたらよいかわかりませんでした。私が得ることができる最高のものはスタックトレースを印刷することでした。これは良いと思いますが、少し多すぎるかもしれません。またdebug.PrintStack()、行番号が必要になるたびに書きたくありませんlog.FatalStackTrace()。このような組み込みの関数やコスチュームではないものがないことにただ驚いています。

また、私がデバッグ/エラー処理を独自に作成したくないのは、特別な衣装処理コードの使用方法を人々に学ばせたくないためです。私は後で人々が私のコードを読んで、

「そうだ、それでエラーを投げてXをする...」

人々が私のコードについて学ぶ必要が少ないほど、より良いです:)



行番号を出力している瞬間、それは私があなたのコードに飛び込む必要があることを意味するので、「人々が私のコードについて学ぶ必要が少ないほど良い」というのは、ここでは議論の余地があります。あなたがすべきことは、明確で簡潔なエラーがあることです。
ウェッシー2014

回答:


122

あなたは含めるカスタムロガー、またはデフォルトのいずれかにフラグを設定することができますLlongfileLshortfile

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)

それで、これが機能するためには、パッケージファイルの1つの上部に設定するだけで、そのパッケージのすべてのファイルで使用できますか?
ピノキオ

4
はい、カスタムログを使用している場合は、のように使用できますvar mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile)
OneOfOne 2014

変数を作成する必要がありますか?goファイルの先頭で単にlog.SetFlags(log.LstdFlags | log.Lshortfile)を実行することはできませんか?エラーが発生しました:expected declaration, found 'INDENT' log実行しようとしたときlog.SetFlags(log.LstdFlags | log.Lshortfile)。それのために変数を作成しなければならないのは、私をいらいらさせるだけですlog.Fatal("string", log.Flag)。しかし、新しい変数ログの作成はうまくいきました。ログ変数などを作成するのは標準的なことですか?
ピノキオ

3
@Pinocchio:そのエラーは、それが有効なGoではないためです。トップレベルでベア関数呼び出しを行うことはできません。init()またはその他のエントリポイントに配置します。
JimB 14

5
あなたはそれを入れなければなりませんfunc init() {}
OneOfOne '07 / 07/18

94

短縮版、 直接組み込まれているものはありませんただし、最小限の学習曲線でそれを実装できます。 runtime.Caller

func HandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log where
        // the error happened, 0 = this function, we don't want that.
        _, fn, line, _ := runtime.Caller(1)
        log.Printf("[error] %s:%d %v", fn, line, err)
        b = true
    }
    return
}

//this logs the function name as well.
func FancyHandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log the where
        // the error happened, 0 = this function, we don't want that.
        pc, fn, line, _ := runtime.Caller(1)

        log.Printf("[error] in %s[%s:%d] %v", runtime.FuncForPC(pc).Name(), fn, line, err)
        b = true
    }
    return
}

func main() {
    if FancyHandleError(fmt.Errorf("it's the end of the world")) {
        log.Print("stuff")
    }
}

playground


11
すでに与えられた答えは問題をきちんと修正しますが、あなたの解決策は私に何か素晴らしいもの、つまりランタイムパッケージの存在を警告しました!素敵なもの:) golang.org/pkg/runtime
Gwyneth Llewelyn

fn割り当てられた変数はruntime.Caller()、実際のファイルではなく、関数参照の名前です。私はfnファイル名ではなく関数と考えています
sshow

1
驚くばかり!ありがとう。これはruntimeパッケージの使用法の良い例です。ログによるデバッグに非常に役立ちます。
8

1

スタックトレースが正確に必要な場合は、https://github.com/ztrue/tracerrを参照してください。

スタックトレースとソースフラグメントの両方を使用してデバッグを高速化し、エラーをより詳細に記録できるようにするために、このパッケージを作成しました。

次にコード例を示します。

package main

import (
    "io/ioutil"
    "github.com/ztrue/tracerr"
)

func main() {
    if err := read(); err != nil {
        tracerr.PrintSourceColor(err)
    }
}

func read() error {
    return readNonExistent()
}

func readNonExistent() error {
    _, err := ioutil.ReadFile("/tmp/non_existent_file")
    // Add stack trace to existing error, no matter if it's nil.
    return tracerr.Wrap(err)
}

そしてここに出力があります: golangエラースタックトレース

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.