如何获取logrus打印pkg /错误堆栈

时间:2019-01-25 16:31:45

标签: go

我正在使用github.com/sirupsen/logrus和github.com/pkg/errors。当我处理由pkg / errors包装或创建的错误时,我在注销中看到的只是错误消息。我想查看堆栈跟踪。

从这个问题https://github.com/sirupsen/logrus/issues/506,我推断出logrus有一些本地方法可以处理pkg / errors。

我该怎么做?

2 个答案:

答案 0 :(得分:0)

推论是错误的。 Logrus实际上并不知道如何处理该错误。 更新:Logrus小组的负责人说,这不是受支持的功能https://github.com/sirupsen/logrus/issues/895#issuecomment-457656556

类似Java的响应 为了以这种方式通用地使用错误处理程序,我编写了一个新版本的Entry,它来自Logrus。如示例所示,使用所需的常用字段创建一个新Entry(示例下方是在处理程序中设置的记录器,该记录器跟踪呼叫者ID。在处理Entry时,将PgkError传递给各层。记录特定错误,例如遇到错误的调用变量,从PkgError.WithError(...)开始,然后添加您的详细信息。

这是一个起点。如果要一般使用此功能,请在PkgErrorEntry上实现所有Entity接口。继续委派给内部条目,但返回一个新的PkgErrorEntry。这样的更改会使该值在替换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")
}

一种Gopher-ish方式 有关更多详细信息,请参见https://www.reddit.com/r/golang/comments/ajby88/how_to_get_stack_traces_in_logrus/

Ben Johnson wrote关于使错误成为您域的一部分。缩写版本是您应将跟踪器属性置于自定义错误中。当直接在您的控制错误下的代码或发生来自第三方库的错误时,立即处理该错误的代码应在自定义错误中添加一个唯一值。此值将作为自定义错误的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
}

我可以使用可重复代码的代码存在的一个问题是,该值很容易与原始上下文背离。例如,说函数的名称从attachRole更改为其他名称,并且函数变长了。 op值可能与函数名称不同。无论如何,这似乎可以满足追踪问题的一般需求,同时也可以处理头等公民的错误。

Go2可能会为此弯下腰,以获得更多类似于Java的响应。敬请关注。 https://go.googlesource.com/proposal/+/refs/changes/97/159497/3/design/XXXXX-error-values.md

答案 1 :(得分:0)

您对Logrus问题的评论是错误的(顺便说一句,似乎来自与Logrus没有关系,对Logrus没有任何贡献的人,实际上不是来自“ Logrus团队”的人。)

很容易提取pkg/errors错误as documented中的堆栈跟踪:

type stackTracer interface {
        StackTrace() errors.StackTrace
}

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

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

从今天开始,当您使用JSON日志记录时,my a pull request of mine was merged with pkg/errors现在变得更加简单:

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

您可以将其强制打印为所需的任何格式,而不是打印每个帧。