zoukankan      html  css  js  c++  java
  • EasyNVR程序异常退出如何通过Go语言捕获fatal错误?

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

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

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

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

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

    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 系统中,修改的代码如下:

    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 系统中,修改的代码如下:

    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函数中调用代码

    elog.RedirectStderr()

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

  • 相关阅读:
    [转]C#程序无法在64位系统上运行之.NET编译的目标平台
    STM32是否可以跑linux
    [转]C/C++ 实现文件透明加解密
    逻辑运算
    STM32F1和STM32F4 区别
    【转】STM32定时器输出比较模式中的疑惑
    Linux rabbitmq的安装和安装amqp的php插件
    跨境电商常用的物流方式
    linux 木马清理过程
    minerd
  • 原文地址:https://www.cnblogs.com/EasyNVR/p/15603911.html
Copyright © 2011-2022 走看看