คุณจะให้โปรแกรม Golang พิมพ์หมายเลขบรรทัดของข้อผิดพลาดที่เพิ่งเรียกได้อย่างไร?


96

ฉันพยายามโยนข้อผิดพลาดในโปรแกรม Golang ของฉันด้วยlog.Fatalแต่log.Fatalไม่พิมพ์บรรทัดที่log.Fatalรันด้วย ไม่มีวิธีเข้าถึงหมายเลขบรรทัดที่เรียกว่า log Fatal? คือมีวิธีรับหมายเลขบรรทัดเมื่อเกิดข้อผิดพลาดหรือไม่?

ฉันพยายามที่จะ google แต่ไม่แน่ใจว่าจะทำอย่างไร สิ่งที่ดีที่สุดที่ฉันจะได้รับคือการพิมพ์สแต็กแทร็กซึ่งฉันคิดว่าดี แต่อาจจะมากเกินไป ฉันไม่ต้องการเขียนdebug.PrintStack()ทุกครั้งที่ฉันต้องการหมายเลขบรรทัดฉันรู้สึกประหลาดใจที่ไม่มีฟังก์ชันในตัวสำหรับสิ่งนี้log.FatalStackTrace()หรือสิ่งที่ไม่ใช่เครื่องแต่งกาย

นอกจากนี้เหตุผลที่ฉันไม่ต้องการสร้างสิ่งที่จัดการแก้ไขข้อบกพร่อง / ข้อผิดพลาดของตัวเองก็เพราะฉันไม่ต้องการให้คนอื่นต้องเรียนรู้วิธีใช้รหัสการจัดการเครื่องแต่งกายพิเศษของฉัน ฉันแค่ต้องการมาตรฐานที่ผู้คนสามารถอ่านรหัสของฉันในภายหลังและเป็นเหมือน

"อ่าตกลงมันเกิดข้อผิดพลาดและทำ X ... "

ยิ่งมีคนเรียนรู้เกี่ยวกับโค้ดของฉันน้อยเท่าไหร่ :)



ขณะที่คุณกำลังพิมพ์หมายเลขบรรทัดหมายความว่าฉันจะต้องดำดิ่งลงไปในโค้ดของคุณดังนั้น "ยิ่งมีคนน้อยต้องเรียนรู้เกี่ยวกับรหัสของฉันมากเท่าไหร่ก็ยิ่งดี" จึงเป็นข้อสงสัยที่นี่ สิ่งที่คุณควรทำคือมีข้อผิดพลาดที่ชัดเจนและรัดกุม
Wessie

คำตอบ:


124

คุณสามารถตั้งค่าแฟล็กบน Logger แบบกำหนดเองหรือค่าเริ่มต้นที่จะรวมLlongfileหรือLshortfile

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

ดังนั้นเพื่อให้ใช้งานได้ฉันต้องตั้งค่าที่ด้านบนของไฟล์แพ็กเกจไฟล์ใดไฟล์หนึ่งและจะพร้อมใช้งานสำหรับไฟล์ทั้งหมดของฉันสำหรับแพ็คเกจนั้น?
Pinocchio

4
ใช่ถ้าคุณใช้บันทึกที่กำหนดเองคุณสามารถใช้มันvar mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile)ได้
OneOfOne

ฉันต้องสร้างตัวแปรจริงๆหรือ ฉันทำ log.SetFlags (log.LstdFlags | log.Lshortfile) ที่ด้านบนของไฟล์ไม่ได้หรือไม่ ฉันได้รับข้อผิดพลาด: เมื่อฉันพยายามที่จะทำexpected declaration, found 'INDENT' log log.SetFlags(log.LstdFlags | log.Lshortfile)มันทำให้ฉันรู้สึกหงุดหงิดที่ต้องสร้างตัวแปรให้มันทำไมไม่มีไฟล์log.Fatal("string", log.Flag). แต่การสร้างบันทึกตัวแปรใหม่ได้ผล เป็นสิ่งมาตรฐานในการสร้างตัวแปรบันทึกและสิ่งของหรือไม่?
Pinocchio

3
@ Pinocchio: ข้อผิดพลาดนั้นเป็นเพราะ Go ไม่ถูกต้องคุณไม่สามารถเรียกใช้ฟังก์ชันเปล่าที่ระดับบนสุดได้ ใส่ไว้ใน init () หรือจุดเริ่มต้นอื่น ๆ
JimB

5
คุณต้องใส่เข้าไปfunc init() {}
OneOfOne

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การใช้งานแพ็กเกจ มีประโยชน์มากสำหรับการดีบักผ่านบันทึก
18 ส.ค.

2

หากคุณต้องการการติดตามสแต็กให้ดูที่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.