2019独角兽企业重金招聘Python工程师标准>>>
当修改 log 库的 out 或者修改 os.Stderr 时,都只能将正常的日志输出,无法输出 panic 的信息。
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 来实现:
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 中有一种实现是这样的:
// 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 下的实现是这样的:
// 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)
}
}