前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >程序异常退出,如何通过Go语言捕获fatal错误?

程序异常退出,如何通过Go语言捕获fatal错误?

作者头像
玖柒的小窝
修改2021-11-21 21:57:39
1.1K0
修改2021-11-21 21:57:39
举报
文章被收录于专栏:各类技术文章~

我们团队经常会对我们现有视频平台比如 EasyNVR、EasyGBS 等进行版本更新以及不同系统的适配测试,在 EasyNVR 测试版本中,出现程序异常退出的情况,但是日志中查找不到对应的错误。

这个问题我们可以通过对 Go 语言捕获错误的功能进行排查和整理。一般情况下,采用defer func(){recover() …} 类似的函数捕获程序中的错误,但是 recover() 函数在以下三种情况下是捕获不到对应的异常:

1.新运行了一个子协程,如果子协程中出现 panic 错误,是无法捕获的;

2.如果在程序中直接 os.Exit(0),对应的 defer 函数也不会运行,整个程序直接退出;

3.如果发生致命错误,recover() 无法捕获,例如以下的代码,并不能被捕获到。

代码语言:javascript
复制
defer func() {
   err := recover()
   if err != nil {
      fmt.Print("err=", err)
   }
}()

a := "hello world"
sh := (*reflect.StringHeader)(unsafe.Pointer(&a))
bh := reflect.SliceHeader{
   Data: sh.Data,
   Len:  sh.Len,
   Cap:  sh.Len,
}
b := *(*[]byte)(unsafe.Pointer(&bh))
b[0] = 'H'
复制代码

运行过程中程序会直接出现一个致命异常,导致整个程序崩溃退出。

但是该种情况下,无法写入到日志,因此在程序运行中只有通过控制台才能看到对应的日志。针对此种情况,需要对代码进行处理。

在 Windows 系统中,修改的代码如下:

代码语言:javascript
复制
package elog

import (
   "os"
   "syscall"
)

var (
   kernel32         = syscall.MustLoadDLL("kernel32.dll")
   procSetStdHandle = kernel32.MustFindProc("SetStdHandle")
)

func setStdHandle(stdhandle int32, handle syscall.Handle) error {
   r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0)
   if r0 == 0 {
      if e1 != 0 {
         return error(e1)
      }
      return syscall.EINVAL
   }
   return nil
}

// RedirectStderr to the file passed in
func RedirectStderr() (err error) {
   logFile, err := os.OpenFile("./test-error.log", os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644)
   if err != nil {
      return
   }
   err = setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(logFile.Fd()))
   if err != nil {
      return
   }
   // SetStdHandle does not affect prior references to stderr
   os.Stderr = logFile
   return
}
复制代码

在 Linux 系统中,修改的代码如下:

代码语言:javascript
复制
package elog

import (
   "os"
   "syscall"
)

// RedirectStderr to the file passed in
func RedirectStderr() (err error) {
   logFile, err := os.OpenFile("./test-error.log", os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644)
   if err != nil {
      return
   }
   err = syscall.Dup3(int(logFile.Fd()), int(os.Stderr.Fd()),0)
   if err != nil {
      return
   }
   return
}
复制代码

main函数中调用代码

代码语言:javascript
复制
elog.RedirectStderr()
复制代码

最终如果出现 fatal 代码,就写入到 test-error.log 中,也就是以下文件:

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档