Our team often updates the version of our existing video platforms, such as EasyNVR and EasyGBS, and tests the adaptation of different systems. In the test version of EasyNVR, abnormal program exit occurred, but the corresponding error could not be found in the log.

This problem can be investigated and sorted out by the Go language’s error capture function. In general, defer func(){recover()… } functions like recover catch errors in programs, but recover() does not catch the corresponding exception in three cases:

1. A new subcoroutine is running. If panic occurs in the subcoroutine, it cannot be caught;

2. If you direct os.exit (0) in the program, the corresponding defer function will not run and the whole program will Exit directly.

3. Recover () cannot be caught if a fatal error occurs, such as the following code.

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'Copy the code

A fatal exception will appear directly during the operation of the program, resulting in the crash and exit of the entire program.

However, in this case, it cannot be written to the log, so the corresponding log can only be seen through the console while the program is running. In this case, the code needs to be handled.

In Windows, the modified code is as follows:

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 }Copy the code

In Linux, the modified code is as follows:

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 }Copy the code

Call code in the main function

elog.RedirectStderr()
Copy the code

Eventually, if fatal code is present, it is written to test-error.log, which is the following file: