Wie bringt man ein Golang-Programm dazu, die Zeilennummer des gerade aufgerufenen Fehlers auszudrucken?

92

Ich habe versucht, Fehler in mein Golang-Programm mit zu werfen, druckt log.Fatalaber log.Fatalnicht auch die Zeile, in der das ausgeführt log.Fatalwurde. Gibt es keine Möglichkeit, auf die Zeilennummer log.Fatal zuzugreifen? dh gibt es eine Möglichkeit, die Zeilennummer zu erhalten, wenn ein Fehler ausgegeben wird?

Ich habe versucht, dies zu googeln, war mir aber nicht sicher, wie. Das Beste, was ich bekommen konnte, war das Drucken der Stapelspur , was meiner Meinung nach gut ist, aber möglicherweise etwas zu viel. Ich möchte auch nicht debug.PrintStack()jedes Mal schreiben, wenn ich die Zeilennummer brauche. Ich bin nur überrascht, dass es dafür keine eingebaute Funktion gibt log.FatalStackTrace()oder etwas, das kein Kostüm ist.

Der Grund, warum ich kein eigenes Debugging- / Fehlerbehandlungsmaterial erstellen möchte, ist, dass ich nicht möchte, dass die Leute lernen müssen, wie sie meinen speziellen Kostümhandhabungscode verwenden. Ich möchte nur einen Standard, in dem die Leute meinen Code später lesen und so sein können

"ah ok, also wirft es einen Fehler und macht X ..."

Je weniger Leute über meinen Code lernen müssen, desto besser :)

Pinocchio
quelle
In dem Moment, in dem Sie Zeilennummern drucken, bedeutet dies, dass ich in Ihren Code eintauchen muss. Je weniger Leute über meinen Code lernen müssen, desto besser ist hier umstritten. Was Sie tun sollten, ist klare und präzise Fehler zu haben.
Wessie

Antworten:

121

Sie können die Flags entweder für einen benutzerdefinierten Logger oder standardmäßig für oder festlegenLlongfileLshortfile

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)
JimB
quelle
Damit dies funktioniert, muss ich das nur oben in eine der Paketdateien setzen und es wird für alle meine Dateien für dieses Paket verfügbar sein?
Pinocchio
4
Ja, wenn Sie ein benutzerdefiniertes Protokoll verwenden, können Sie es wie folgt verwenden var mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile).
OneOfOne
muss ich wirklich eine Variable erstellen? Ich kann nicht einfach log.SetFlags (log.LstdFlags | log.Lshortfile) oben in meiner go-Datei ausführen. Ich erhalte eine Fehlermeldung: expected declaration, found 'INDENT' logWenn ich es versuche log.SetFlags(log.LstdFlags | log.Lshortfile). Es irritiert mich nur, eine Variable dafür erstellen zu müssen, warum kann es keine geben log.Fatal("string", log.Flag). Das Erstellen eines neuen Variablenprotokolls hat jedoch funktioniert. Ist es Standard, Protokollvariablen und ähnliches zu erstellen?
Pinocchio
3
@Pinocchio: Dieser Fehler ist, weil es nicht gültig ist. Go, Sie können keinen nackten Funktionsaufruf auf der obersten Ebene haben. Setzen Sie es in init () oder einen anderen Einstiegspunkt.
JimB
5
Sie müssen es infunc init() {}
OneOfOne
92

Kurzfassung, Es ist nichts direkt eingebautSie können es jedoch mit einer minimalen Lernkurve implementieren 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

OneOfOne
quelle
11
Während die bereits gegebene Antwort das Problem ordentlich behebt, hat mich Ihre Lösung auf die Existenz von etwas Großartigem aufmerksam gemacht - dem Laufzeitpaket! Schöne
Gwyneth Llewelyn
Die von fnzugewiesene Variable runtime.Caller()ist eigentlich der Name der Datei, keine Funktionsreferenz. Ich stelle mir fn als Funktion vor, nicht als Dateinamen .
Show
1
Genial! Vielen Dank. Dies ist ein großartiges Beispiel für die runtimeVerwendung von Paketen. Sehr hilfreich beim Debuggen durch Protokolle.
18. August
1

Wenn Sie genau einen Stack-Trace benötigen, besuchen Sie https://github.com/ztrue/tracerr

Ich habe dieses Paket erstellt, damit sowohl Stack-Trace- als auch Quellfragmente schneller debuggen und Fehler mit viel mehr Details protokollieren können.

Hier ist ein Codebeispiel:

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)
}

Und hier ist die Ausgabe: Golang Fehler Stack Trace

Johan
quelle