3 回答

TA貢獻(xiàn)1877條經(jīng)驗(yàn) 獲得超1個(gè)贊
據(jù)我所知,您無法將 panic 的輸出重定向到遠(yuǎn)離標(biāo)準(zhǔn)錯(cuò)誤或您的記錄器。您能做的最好的事情是將標(biāo)準(zhǔn)錯(cuò)誤重定向到您可以在外部或程序內(nèi)部執(zhí)行的文件。
對(duì)于我的rclone程序,我重定向了標(biāo)準(zhǔn)錯(cuò)誤以將所有內(nèi)容捕獲到一個(gè)選項(xiàng)的文件中,不幸的是,這在跨平臺(tái)方式中并不是特別容易做到。這是我是如何做到的(請(qǐng)參閱 redirect*.go 文件)
對(duì)于 linux/unix
// 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)
}
}
和窗戶
// 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
}

TA貢獻(xiàn)1860條經(jīng)驗(yàn) 獲得超9個(gè)贊
您可以使用recover()從同一個(gè) goroutine 中恢復(fù)恐慌。當(dāng)調(diào)用recover()延遲方法時(shí)(請(qǐng)記住,延遲方法仍然會(huì)被調(diào)用,即使在panic()ing 時(shí)),它將返回panic()作為參數(shù)傳遞給最后一次調(diào)用的任何內(nèi)容(或者nil當(dāng)程序沒有發(fā)生恐慌時(shí))。
defer func() {
if x := recover(); x != nil {
// recovering from a panic; x contains whatever was passed to panic()
log.Printf("run time panic: %v", x)
// if you just want to log the panic, panic again
panic(x)
}
}()
panic("foo");
但是請(qǐng)注意,您無法從不同 goroutine 中觸發(fā)的恐慌中恢復(fù)(感謝 JimB 的提示)。使用單個(gè)recover()從任何 goroutine 的恐慌中恢復(fù)是不可能的。

TA貢獻(xiàn)1806條經(jīng)驗(yàn) 獲得超8個(gè)贊
擴(kuò)展@nick-craig-wood 的回答:如果您使用的是 Linux,您可以生成一個(gè) logger(1) 實(shí)例并將 stderr 重定向到它。這樣你就可以在系統(tǒng)日志中獲得完整的回溯。這就是gocryptfs所做的:
// redirectStdFds redirects stderr and stdout to syslog; stdin to /dev/null
func redirectStdFds() {
// stderr and stdout
pr, pw, err := os.Pipe()
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not create pipe: %v\n", err)
return
}
tag := fmt.Sprintf("gocryptfs-%d-logger", os.Getpid())
cmd := exec.Command("logger", "-t", tag)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = pr
err = cmd.Start()
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not start logger: %v\n", err)
}
pr.Close()
err = syscall.Dup2(int(pw.Fd()), 1)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stdout dup error: %v\n", err)
}
syscall.Dup2(int(pw.Fd()), 2)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stderr dup error: %v\n", err)
}
pw.Close()
// stdin
nullFd, err := os.Open("/dev/null")
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not open /dev/null: %v\n", err)
return
}
err = syscall.Dup2(int(nullFd.Fd()), 0)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stdin dup error: %v\n", err)
}
nullFd.Close()
}
- 3 回答
- 0 關(guān)注
- 314 瀏覽
添加回答
舉報(bào)