美文网首页
如何把panic信息重定向,syscall.Dup2的使用

如何把panic信息重定向,syscall.Dup2的使用

作者: 哆啦在这A梦在哪 | 来源:发表于2020-03-12 10:07 被阅读0次

如何把panic信息重定向

根据“墨菲定律”,我们编写的后台的服务都有出现crash的可能,一种情况是Go的后台服务我们经常也会遇到panic的情况。出问题不可怕,我们需要分析并解决问题,不过panic处理的信息,默认是直接标准输出的,我们希望能捕获它指向我们特定的文件以便能做后续问题的跟踪排查,而不是一次性输出难以跟踪。

我们一个通用的方法是

err := execFunc()
if err != nil {
    outputToFile(err)
}

但有一些第三方库会使用panic/recover机制作为其内部的异常控制方式,这样我们在外面是难以察觉的,异常信息可能就直接打到我们的标准输出那里了,除非你在执行程序之前,使用类似linux的 ./test >> panic.log ,否则我们会很大机会与重要的跟踪信息擦肩而过。(跨平台到windows可能不适用)

所以,如何把panic的信息灵活地“重定向”呢?

实现思路一般是:

1、既然panic使用的的是标准输出,我们可以使用自定义的文件file引用取代go里头的os.Stdout 和 os.Stderr

2、引起panic并测试重定向的正确性

3、windows里面没有stdout和stderr的输出方式,也没办法像unix那样使用“>>”进行标准输出的重新向,这个如何破?

我们先试试一个简单的例子:

package main

import (
    "fmt";
    "os";
)

const panicFile = "/tmp/panic.log"

func InitPanicFile() error {
    log.Println("init panic file in unix mode")
    file, err := os.OpenFile(panicFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        return err
    }

    os.Stdout = file
    os.Stderr = file
    return nil
}

func init() {
    err := pc.InitPanicFile()
    if err != nil {
        println(err)
    }
}

func testPanic() {
    panic("test panic")
}

func main() {
    testPanic()
}

这个例子,我们尝试使用 os.Stdout = fileos.Stderr = file 来“强制”转换,但我们运行程序后,发现不起作用, /tmp/panic.log 没有任何信息流入,panic信息照样输出到标准输出那里。

关于原因,Rob是这样说的:

image.png

看来是把变量直接赋值到底层是不行的,图上所说,推荐使用syscall.Dup的方式。我们再改写下 InitPanicFile() 函数:

func InitPanicFile() error {
    log.Println("init panic file in unix mode")
    file, err := os.OpenFile(panicFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        println(err)
        return err
    }
    if err = syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
        return err
    }
    return nil
}

我们运行程序,发现panic正常定向到我们的文件里面去了:

$ tail -f /tmp/panic.log

panic: test panic

goroutine 1 [running]:

... ...
... ...

不过经过实践,上面的代码是有些bug的,原因是我们上面的file是一个局部变量,放系统发生gc的时候,会触发file里面的 runtime.SetFinalizer(f.file, (*file).close), 会引起句柄会被回收, 如果我们代码是长期运行在后台的话,建议代码调整如下的形式:

var globalFile *os.File

func InitPanicFile() error {
    log.Println("init panic file in unix mode")
    file, err := os.OpenFile(panicFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    globalFile = file
    if err != nil {
        println(err)
        return err
    }
    if err = syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
        return err
    }
    return nil
}

接下来,我们要延伸思考下,如果服务是运行在windows上面该如何破?

使用syscall.Dup2的例子windows下会编译直接报错:

undefined: syscall.Dup2

... ...

syscall.Dup2 is a linux/OSX only thing. there's no windows equivalent。

记得我前面的文件,介绍过go调用DLL的方法 《使用Go结合windows dll开发程序》 ,其实我们也可以想到,可以直接使用DLL的调用达到功能效果:

代码如下:

package main

import (
    "log"
    "os"
    "syscall"
)

const (
    kernel32dll = "kernel32.dll"
)

const panicFile = "C:/panic.log"

var globalFile *os.File

func InitPanicFile() error {
    log.Println("init panic file in windows mode")
    file, err := os.OpenFile(panicFile, os.O_CREATE|os.O_APPEND, 0666)
    globalFile = file
    if err != nil {
        return err
    }
    kernel32 := syscall.NewLazyDLL(kernel32dll)
    setStdHandle := kernel32.NewProc("SetStdHandle")
    sh := syscall.STD_ERROR_HANDLE
    v, _, err := setStdHandle.Call(uintptr(sh), uintptr(file.Fd()))
    if v == 0 {
        return err
    }
    return nil
}

func init() {
    err := pc.InitPanicFile()
    if err != nil {
        println(err)
    }
}

func testPanic() {
    panic("test panic")
}

func main() {
    testPanic()
}

然后我们把编译后的代码在windows下运行,panic信息也能正常重定向到指定文件上了。

相关文章

  • 如何把panic信息重定向,syscall.Dup2的使用

    如何把panic信息重定向 根据“墨菲定律”,我们编写的后台的服务都有出现crash的可能,一种情况是Go的后台服...

  • 重定向

    重定向分为输入重定向和输出重定向。输入重定向是指把文件导入到命令中,而输出重定向则是指把原本要输出到屏幕的数据信息...

  • go 的标准日志库 Log

    我们常使用 Go log 以下三组函数: Print/Printf/Println : 打印日志信息 Panic/...

  • 05-内建方法-panic/recover

    1. panic 单独使用panic,发现错误后程序退出 1.1 代码 1.2 结果 一个悲伤的故事,panic ...

  • 第3章 管道符、重定向与环境变量

    引言 3.1输入输出重定向 输入重定向 是指把文件导入到命令中输出重定向 是指把原本要输出到屏幕的数据信息写入...

  • Linux学习笔记3-管道符、重定向与环境变量

    输入输出重定向 输入重定向是指把文件导入命令中,而输出重定向则是指把原本要输出到屏幕的数据信息写入到指定文件中。输...

  • Liunx 管道符、重定向与环境变量

    输入输出重定向   输入重定向是指把文件导入到命令中,而输出重定向则是把原本要输出到屏幕的数据信息写入到指定文件中...

  • Stackoverflow热门问题

    1. JavaScript如何重定向到其他网页 如何使用JavaScript将用户从一个网页重定向到另一个网页? ...

  • day013-Linux学习 标准输入、输出 重定向

    一、重定向概述 1.什么是重定向 将原本要输出到屏幕的数据信息,重新定向到某个指定的文件中。 2.为何要使用重定向...

  • 重定向、管道符和环境变量

    重定向 输入重定向是指把文件导入到命令中,而输出重定向是指将原本要输出到屏幕的数据信息写入到指定文件中。输出重定向...

网友评论

      本文标题:如何把panic信息重定向,syscall.Dup2的使用

      本文链接:https://www.haomeiwen.com/subject/fklyjhtx.html