Go语言 如何让logrus打印pkg/错误堆栈

sr4lhrrt  于 2022-12-20  发布在  Go
关注(0)|答案(3)|浏览(373)

I'm using github.com/sirupsen/logrus and github.com/pkg/errors. When I hand an error wrapped or created from pkg/errors, all I see in the log out is the error message. I want to see the stack trace.
从这个问题https://github.com/sirupsen/logrus/issues/506中,我推断logrus有一些处理pkg/errors的本地方法。
我该怎么做呢?

2lpgd968

2lpgd9681#

关于你的洛格鲁斯问题的评论是不正确的(顺便说一句,似乎来自一个与洛格鲁斯没有任何联系的人,他对洛格鲁斯没有任何贡献,所以实际上不是来自“洛格鲁斯团队”)。
很容易在pkg/errors错误中提取堆栈跟踪,如文档所示:

type stackTracer interface {
        StackTrace() errors.StackTrace
}

这意味着使用logrus记录堆栈跟踪的最简单方法是:

if stackErr, ok := err.(stackTracer); ok {
    log.WithField("stacktrace", fmt.Sprintf("%+v", stackErr.StackTrace()))
}

到目前为止,当my a pull request of mine was merged with pkg/errors时,如果您使用JSON日志记录,这将变得更加容易:

if stackErr, ok := err.(stackTracer); ok {
    log.WithField("stacktrace", stackErr.StackTrace())
}

这将产生一个类似于“%+v”的日志格式,但没有换行符或制表符,每个字符串一个日志条目,以便轻松地封送到JSON数组中。
当然,这两个选项都强制您使用pkg/errors定义的格式,这并不总是理想的,因此,您可以迭代堆栈跟踪,并生成自己的格式,可能生成一个易于封送到JSON的格式。

if err, ok := err.(stackTracer); ok {
        for _, f := range err.StackTrace() {
                fmt.Printf("%+s:%d\n", f, f) // Or your own formatting
        }
}

您可以将每个帧强制转换为您喜欢的任何格式,而不是打印每个帧。

cxfofazt

cxfofazt2#

推断是错误的。Logrus实际上并不知道如何处理错误。更新Logrus团队官员表示,这不是一个受支持的功能,https://github.com/sirupsen/logrus/issues/895#issuecomment-457656556。

Java风格的响应为了以这种方式普遍使用错误处理程序,我编写了一个新版本的Entry,它来自Logrus。(下面的例子是在处理程序中设置的一个记录器,用于跟踪调用者ID。在处理Entry时,通过层传递PgkError。当需要记录特定错误时,就像调用遇到错误的变量一样,从PkgError.WithError(...)开始,然后添加详细信息。

这是一个起点。如果您希望在一般情况下使用它,请在PkgErrorEntry上实现所有Entity接口。继续委托给内部条目,但返回一个新的PkgErrorEntry。这样的更改会使值true在Entry的替换中下降。

package main

import (
    "fmt"
    "github.com/sirupsen/logrus"
    "strings"

    unwrappedErrors "errors"
    "github.com/pkg/errors"
)

// PkgErrorEntry enables stack frame extraction directly into the log fields.
type PkgErrorEntry struct {
    *logrus.Entry

    // Depth defines how much of the stacktrace you want.
    Depth int
}

// This is dirty pkg/errors.
type stackTracer interface {
    StackTrace() errors.StackTrace
}

func (e *PkgErrorEntry) WithError(err error) *logrus.Entry {
    out := e.Entry

    common := func(pError stackTracer) {
        st := pError.StackTrace()
        depth := 3
        if e.Depth != 0 {
            depth = e.Depth
        }
        valued := fmt.Sprintf("%+v", st[0:depth])
        valued = strings.Replace(valued, "\t", "", -1)
        stack := strings.Split(valued, "\n")
        out = out.WithField("stack", stack[2:])
    }

    if err2, ok := err.(stackTracer); ok {
        common(err2)
    }

    if err2, ok := errors.Cause(err).(stackTracer); ok {
        common(err2)
    }

    return out.WithError(err)
}

func someWhereElse() error {
    return unwrappedErrors.New("Ouch")
}

func level1() error {
    return level2()
}

func level2() error {
    return errors.WithStack(unwrappedErrors.New("All wrapped up"))
}

func main() {
    baseLog := logrus.New()
    baseLog.SetFormatter(&logrus.JSONFormatter{})
    errorHandling := PkgErrorEntry{Entry: baseLog.WithField("callerid", "1000")}

    errorHandling.Info("Hello")

    err := errors.New("Hi")
    errorHandling.WithError(err).Error("That should have a stack.")

    err = someWhereElse()
    errorHandling.WithError(err).Info("Less painful error")

    err = level1()
    errorHandling.WithError(err).Warn("Should have multiple layers of stack")
}

类似地鼠的方式有关详细信息,请参阅https://www.reddit.com/r/golang/comments/ajby88/how_to_get_stack_traces_in_logrus/

BenJsonwrote关于使错误成为域的一部分。缩写版本是您应该将跟踪器属性放在自定义错误上。当直接受您控制的代码出错或发生来自第三方库的错误时,立即处理错误的代码应该将唯一值放在自定义错误中。此值将作为自定义错误的Error() string实现的一部分打印。
当开发者得到日志文件后,他们就可以使用grep方法来获取那个唯一值的代码库。Ben说:“最后,我们需要能够向操作员提供所有这些信息以及逻辑堆栈跟踪,这样他们就可以调试问题。Go语言已经提供了一个简单的方法error.Error()来打印错误信息,这样我们就可以利用它了。”
这是本的例子

// attachRole inserts a role record for a user in the database
func (s *UserService) attachRole(ctx context.Context, id int, role string) error {
    const op = "attachRole"
    if _, err := s.db.Exec(`INSERT roles...`); err != nil {
        return &myapp.Error{Op: op, Err: err}
    }
    return nil
}

我使用grep代码的一个问题是,值很容易偏离原始上下文。例如,假设函数的名称从attachRole更改为其他名称,并且函数更长。操作值可能会偏离函数名称。无论如何,这似乎满足了跟踪问题的一般需要,同时将错误视为一级公民。
Go2可能会对此做出更像Java的回应。

flseospp

flseospp3#

使用自定义挂钩提取堆栈跟踪

import (
    "fmt"
    "github.com/pkg/errors"
    "github.com/sirupsen/logrus"
)

type StacktraceHook struct {
}

func (h *StacktraceHook) Levels() []logrus.Level {
    return logrus.AllLevels
}

func (h *StacktraceHook) Fire(e *logrus.Entry) error {
    if v, found := e.Data[logrus.ErrorKey]; found {
        if err, iserr := v.(error); iserr {
            type stackTracer interface {
                StackTrace() errors.StackTrace
            }
            if st, isst := err.(stackTracer); isst {
                stack := fmt.Sprintf("%+v", st.StackTrace())
                e.Data["stacktrace"] = stack
            }
        }
    }
    return nil
}

func main() {
    logrus.SetFormatter(&logrus.TextFormatter{DisableQuote: true})
    logrus.AddHook(&StacktraceHook{})

    logrus.WithError(errors.New("Foo")).Error("Wrong")
}

产出

time=2009-11-10T23:00:00Z level=error msg=Wrong error=Foo stacktrace=
main.main
    /tmp/sandbox1710078453/prog.go:36
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1594

相关问题