當修改 log 庫的 out 或者修改 os.Stderr 時,都只能將正常的日誌輸出,沒法輸出 panic 的信息。linux
f, err := os.OpenFile("/tmp/err.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) if err != nil { panic(err) } defer f.Close() log.SetOutput(f) os.Stderr = f log.Printf("test log file at %v", time.Now()) // 能夠輸出到文件中 panic("test panic when logging.") // 不能輸出到文件中
這時須要將標準錯誤輸出重定向到咱們打開的文件中,在 linux 下可使用 syscall.Dup2 來實現:golang
f, err := os.OpenFile("/tmp/err.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) if err != nil { panic(err) } defer f.Close() syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd())) // 將 stderr 重定向到 f log.Printf("test log file at %v", time.Now()) panic("test panic when logging.") // 會輸出到 f 中
syscall.Dup2 在 window 下會報錯。stackoverflow 中有一種實現是這樣的:windows
// Log the panic under windows to the log file // // Code from minix, via // // http://play.golang.org/p/kLtct7lSUg //+build windows package main import ( "log" "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(f *os.File) { err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd())) if err != nil { log.Fatalf("Failed to redirect stderr to file: %v", err) } // SetStdHandle does not affect prior references to stderr os.Stderr = f }
相應的 linux 下的實現是這樣的:ui
// Log the panic under unix to the log file //+build unix package main import ( "log" "os" "syscall" ) // redirectStderr to the file passed in func redirectStderr(f *os.File) { err := syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd())) if err != nil { log.Fatalf("Failed to redirect stderr to file: %v", err) } }