How to handle panic error in Golang

ref: https://ednsquare.com/story/learn-go-panic-and-recover-in-golang------1HSVNg


func main() {



NullPointer(nil)
time.Sleep(2*time.Second)
fmt.Println("After panic")

}

type Test struct {
X int
}

func NullPointer(v *Test) {
fmt.Println(v.X)
}


Đoạn code trên dễ dàng gây ra lỗi panic và stop hàm main

panic: runtime error: invalid memory address or nil pointer dereference


Vậy chúng ta cần làm gì để tránh gây chết hàm main đây

Nếu là java hoặc ngôn ngữ khác thì chúng ta đã có try catch finally 

Còn trong golang bạn sẽ có một cách trông đẹp mắt hơn


func main() {

defer func() {
if err := recover(); err != nil {
logrus.Errorln(err)
debug.PrintStack()
}
}()

NullPointer(nil)
time.Sleep(2*time.Second)
fmt.Println("After panic")

}

type Test struct {
X int
}

func NullPointer(v *Test) {
fmt.Println(v.X)
}


Theo cách này thì trong hàm main chúng ta đã thêm một đoạn code để catch lỗi panic null pointer và in ra lỗi và Stacktrace nó luôn

và kết quả như bên dưới

ERRO[0000] runtime error: invalid memory address or nil pointer dereference 

goroutine 1 [running]:

runtime/debug.Stack(0x4a80c0, 0xc000060220, 0x0)

        /home/bonh/golang/go/src/runtime/debug/stack.go:24 +0x9d

runtime/debug.PrintStack()

        /home/bonh/golang/go/src/runtime/debug/stack.go:16 +0x22

main.main.func1()

        /home/bonh/Workspace/go/demo/main.go:15 +0x95

panic(0x4c98c0, 0x5a20c0)

        /home/bonh/golang/go/src/runtime/panic.go:522 +0x1b5

main.NullPointer(...)

        /home/bonh/Workspace/go/demo/main.go:30

main.main()

        /home/bonh/Workspace/go/demo/main.go:19 +0x44


Process finished with exit code 0


Bạn để ý thấy dòng cuối cùng là hàm main đã exit code = 0 -> không bị chết hàm main đột ngột


Tuy nhiên nếu chúng ta chạy hàm NullPointer(nil)

với go routine thì chúng ta lại không bắt được panic như đoạn code dưới đây



func main() {

defer func() {
if err := recover(); err != nil {
logrus.Errorln(err)
debug.PrintStack()
}
}()

go NullPointer(nil)
time.Sleep(2*time.Second)
fmt.Println("After panic")

}

type Test struct {
X int
}

func NullPointer(v *Test) {
fmt.Println(v.X)
}

Vì lời gọi hàm defer của chúng ta không thể bắt được panic error trong một routine khác

nếu cố tình code như trên và chạy thì chúng ta nhận đc lỗi sau :


panic: runtime error: invalid memory address or nil pointer dereference

[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4ac3b6]

goroutine 5 [running]:
main.NullPointer(0x0)
        /home/bonh/Workspace/go/demo/main.go:30 +0x26
created by main.main
        /home/bonh/Workspace/go/demo/main.go:19 +0x62

Process finished with exit code 2

Bạn để ý thấy hàm main exit code = 2

Vậy nếu như này thì tính sao => đơn giản chỉ cần ném đoạn code chạy routine vào trong 1 hàm khác là chúng ta lại bắt được panic ở hàm main ngay

Đây là đoạn code bắt panic khi dùng go routine


func main() {

defer func() {
if err := recover(); err != nil {
logrus.Errorln(err)
debug.PrintStack()
}
}()
go NullPointer(nil)
time.Sleep(2 * time.Second)
fmt.Println("After panic")

}

type Test struct {
X int
}

func NullPointer(v *Test) {
defer func() {
if err := recover(); err != nil {
logrus.Errorln("inside go routine", err)
debug.PrintStack()
}
}()
fmt.Println(v.X)
}


Vậy túm lại chúng ta cần phải đặt defer để bắt panic trước mỗi hàm được chạy bởi go routine





Comments

Popular posts from this blog

Fixing the DeepSpeed Import Error While Fine-Tuning the Qwen Model

Amazon Linux 2023 - User data configuration for launch templates to connect to the EKS cluster

How to create ISM policy and rotate logs in opensearch