Come si ottiene un programma Golang per stampare il numero di riga dell'errore che ha appena chiamato?


94

Stavo cercando di lanciare errori nel mio programma Golang con log.Fatalma log.Fatalnon stampa anche la riga in cui è log.Fatalstato eseguito. Non è possibile accedere al numero di riga che ha chiamato log.Fatal? cioè c'è un modo per ottenere il numero di riga quando si genera un errore?

Stavo cercando di google questo ma non ero sicuro di come. La cosa migliore che ho potuto ottenere è stata stampare la traccia dello stack , che immagino sia buona ma potrebbe essere un po 'troppo. Inoltre non voglio scrivere debug.PrintStack()ogni volta che ho bisogno del numero di riga, sono solo sorpreso che non ci sia alcuna funzione incorporata per questo tipo log.FatalStackTrace()o qualcosa che non sia costume.

Inoltre, il motivo per cui non voglio creare le mie cose di debug / gestione degli errori è perché non voglio che le persone debbano imparare a usare il mio codice speciale per la gestione dei costumi. Voglio solo qualcosa di standard in cui le persone possano leggere il mio codice più tardi ed essere come

"ah ok, so its throwing an error and doing X..."

The less people have to learn about my code the better :)



Nel momento in cui stai stampando i numeri di riga, significa che dovrò immergermi nel tuo codice, quindi "Meno persone devono imparare sul mio codice, meglio è" qui. Quello che dovresti fare è avere errori chiari e concisi.
Wessie

Risposte:


122

È possibile impostare i flag su un logger personalizzato o il valore predefinito per includere LlongfileoLshortfile

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

So, for this to work I only need to set that at the top of one of the package files and it will available for all my files for that package?
Pinocchio

4
Yes, if you're using a custom log you can use it like var mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile).
OneOfOne

devo davvero creare una variabile? Non posso semplicemente fare log.SetFlags (log.LstdFlags | log.Lshortfile) all'inizio del mio file go? Ottengo un errore: expected declaration, found 'INDENT' logquando provo a fare log.SetFlags(log.LstdFlags | log.Lshortfile). Mi irrita semplicemente dover creare una variabile, perché non può esserci un file log.Fatal("string", log.Flag). Ma la creazione di un nuovo registro delle variabili ha funzionato. È una cosa standard creare variabili di registro e cose del genere?
Pinocchio

3
@ Pinocchio: Questo errore è dovuto al fatto che non è valido Go, non puoi avere una semplice chiamata di funzione al livello più alto. Mettilo in init () o in qualche altro punto di ingresso.
JimB

5
devi metterlo infunc init() {}
OneOfOne

94

Versione breve, non c'è niente di direttamente integrato, tuttavia puoi implementarlo con una curva di apprendimento minima usando 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
Mentre la risposta già fornita risolve il problema in modo ordinato, la tua soluzione mi ha avvisato dell'esistenza di qualcosa di fantastico: il pacchetto runtime! Roba adorabile :) golang.org/pkg/runtime
Gwyneth Llewelyn

La fnvariabile assegnata da runtime.Caller()è effettivamente il nome del file, non un riferimento a una funzione. Penso a fn come funzione, non al nome del file .
sshow

1
Eccezionale! Grazie. Questo è un ottimo esempio di runtimeutilizzo del pacchetto. Molto utile per il debug tramite i log.
18 agosto

1

Se hai bisogno esattamente di una traccia dello stack, dai un'occhiata a https://github.com/ztrue/tracerr

Ho creato questo pacchetto per avere sia la traccia dello stack che i frammenti di origine per poter eseguire il debug più velocemente e registrare gli errori con molti più dettagli.

Ecco un esempio di codice:

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

Ed ecco l'output: traccia dello stack degli errori golang

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.