Bagaimana Anda mendapatkan program Golang untuk mencetak nomor baris dari kesalahan yang baru saja dipanggil?

96

Saya mencoba membuang kesalahan dalam program Golang saya dengan log.Fataltetapi, log.Fataltidak juga mencetak baris di mana log.Fatalitu dijalankan. Apakah tidak ada cara untuk mendapatkan akses ke nomor baris yang disebut log.Fatal? yaitu apakah ada cara untuk mendapatkan nomor baris saat melakukan kesalahan?

Saya mencoba mencari ini di Google tetapi tidak yakin caranya. Hal terbaik yang bisa saya dapatkan adalah mencetak jejak tumpukan , yang menurut saya bagus tapi mungkin terlalu banyak. Saya juga tidak ingin menulis debug.PrintStack()setiap kali saya membutuhkan nomor baris, saya hanya terkejut tidak ada fungsi bawaan untuk ini log.FatalStackTrace()atau sesuatu yang bukan kostum.

Juga, alasan saya tidak ingin membuat debugging / error handling saya sendiri adalah karena saya tidak ingin orang lain harus belajar bagaimana menggunakan kode penanganan kostum khusus saya. Saya hanya ingin sesuatu yang standar di mana orang dapat membaca kode saya nanti dan menjadi seperti

"ah ok, jadi itu membuat kesalahan dan melakukan X ..."

Semakin sedikit orang yang harus mempelajari kode saya, semakin baik :)

Pinokio
sumber
Saat Anda mencetak nomor baris itu berarti saya harus menyelami kode Anda, jadi "Semakin sedikit orang yang harus belajar tentang kode saya semakin baik" diperdebatkan di sini. Yang harus Anda lakukan adalah memiliki kesalahan yang jelas dan ringkas.
Wessie

Jawaban:

124

Anda dapat mengatur Bendera di Logger kustom, atau default untuk menyertakan LlongfileatauLshortfile

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)
JimB
sumber
Jadi, agar ini berfungsi, saya hanya perlu mengaturnya di bagian atas salah satu file paket dan itu akan tersedia untuk semua file saya untuk paket itu?
Pinokio
4
Ya, jika Anda menggunakan log khusus, Anda dapat menggunakannya seperti var mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile).
OneOfOne
apakah saya benar-benar harus membuat variabel? Saya tidak bisa begitu saja melakukan log.SetFlags (log.LstdFlags | log.Lshortfile) di bagian atas file go saya? Saya mendapatkan kesalahan: expected declaration, found 'INDENT' logketika saya mencoba melakukannya log.SetFlags(log.LstdFlags | log.Lshortfile). Itu hanya mengganggu saya harus membuat variabel untuk itu, mengapa tidak ada log.Fatal("string", log.Flag). Tetapi membuat log variabel baru berhasil. Apakah itu hal standar untuk membuat variabel log dan semacamnya?
Pinokio
3
@ Pinocchio: Kesalahan itu karena Go tidak valid, Anda tidak dapat memiliki pemanggilan fungsi telanjang di tingkat atas. Taruh di init () atau titik masuk lainnya.
JimB
5
Anda harus memasukkannya ke dalamfunc init() {}
OneOfOne
94

Versi pendek, tidak ada yang langsung terpasang, namun Anda dapat menerapkannya dengan kurva pembelajaran minimal menggunakan 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
sumber
11
Sementara jawaban yang sudah diberikan memperbaiki masalah dengan rapi, solusi Anda mengingatkan saya akan adanya sesuatu yang luar biasa - paket runtime! Hal-hal indah :) golang.org/pkg/runtime
Gwyneth Llewelyn
The fnvariabel ditugaskan dari runtime.Caller()sebenarnya nama file, bukan referensi fungsi. Saya menganggap fn sebagai fungsi, bukan nama file .
sshow
1
Hebat! Terima kasih. Ini adalah contoh yang bagus dari runtimepenggunaan paket. Sangat membantu untuk debugging melalui log.
Agustus
2

Jika Anda membutuhkan pelacakan tumpukan yang tepat, lihat di https://github.com/ztrue/tracerr

Saya membuat paket ini agar memiliki pelacakan tumpukan dan fragmen sumber agar dapat men-debug lebih cepat dan mencatat kesalahan dengan lebih banyak detail.

Berikut adalah contoh kode:

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

Dan inilah hasilnya: jejak tumpukan kesalahan golang

Johan
sumber