在golang中捕获恐慌()

时间:2016-01-13 16:32:08

标签: logging go panic

我们有一个大型golang应用程序,它使用记录器(实际上是一个自定义记录器)将输出写入定期轮换的日志文件。

但是,当应用程序崩溃或panic()时,这些消息会出现标准错误。

有没有办法覆盖恐慌功能以使用我们的记录器?

3 个答案:

答案 0 :(得分:11)

据我所知,您无法将输出信号从标准错误重定向到您的记录器。您可以做的最好的事情是将标准错误重定向到您可以在外部或程序内部执行的文件。

对于我的rclone程序,我重定向标准错误以将所有内容捕获到选项上的文件,遗憾的是,这种选项在跨平台方式中并不容易。我是这样做的(参见重定向* .go文件)

对于linux / unix

CollapsingToolbarLayout

和windows

// 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)
    }
}

答案 1 :(得分:7)

您可以使用recover()从同一个goroutine中恢复恐慌。在延迟方法中调用recover()时(请记住仍会调用延迟方法,即使在panic()时),它将返回作为参数传递给最后panic()调用的内容(或当程序没有恐慌时nil

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");

请注意,您无法从在不同goroutine中触发的恐慌中恢复(感谢JimB提示)。使用单个recover()从任何goroutine中恢复恐慌是不可能的。

答案 2 :(得分:0)

扩大@ nick-craig-wood的答案: 如果你在Linux上,你可以生成一个logger(1)实例并将stderr重定向到它。这样你就可以完全回溯到syslog中。这就是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()
}
相关问题