为了账号安全,请及时绑定邮箱和手机立即绑定

如何让 logrus 打印 pkg/errors 堆栈

如何让 logrus 打印 pkg/errors 堆栈

Go
喵喵时光机 2023-06-01 18:19:46
我正在使用 github.com/sirupsen/logrus 和 github.com/pkg/errors。当我提交一个从 pkg/errors 包装或创建的错误时,我在注销中看到的只是错误消息。我想查看堆栈跟踪。我推断 logrus 有一些处理 pkg/errors 的本机方法。我怎样才能做到这一点?
查看完整描述

3 回答

?
繁华开满天机

TA贡献1816条经验 获得超4个赞

(顺便说一句,似乎来自与 Logrus 没有任何关系且对 Logrus 没有做出任何贡献的人,因此实际上不是来自“Logrus 团队”)。


如记录的那样pkg/errors,很容易在错误中提取堆栈跟踪:


type stackTracer interface {

        StackTrace() errors.StackTrace

}

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


if stackErr, ok := err.(stackTracer); ok {

    log.WithField("stacktrace", fmt.Sprintf("%+v", stackErr.StackTrace()))

}

从今天开始,当我的一个拉取请求与 合并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

        }

}

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


查看完整回答
反对 回复 2023-06-01
?
DIEA

TA贡献1820条经验 获得超2个赞

推论是错误的。Logrus 实际上并不知道如何处理错误。 

类似 Java 的响应 为了以这种方式普遍使用错误处理程序,我编写了一个新版本的 Entry,它来自 Logrus。如示例所示,使用您想要的任何常用字段创建一个新条目(示例下方是在处理程序中设置的记录器,用于跟踪调用者 ID。在处理条目时通过层传递 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")

}

Gopher-ish 方式 请参阅https://www.reddit.com/r/golang/comments/ajby88/how_to_get_stack_traces_in_logrus/了解更多详情。


Ben Johnson写过关于让错误成为你领域的一部分。一个简化版本是您应该将跟踪器属性放在自定义错误上。当直接受您控制的代码出错或发生来自第 3 方库的错误时,立即处理错误的代码应将唯一值放入自定义错误中。该值将作为自定义错误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-able 代码的一个问题是值很容易偏离原始上下文。例如,假设函数的名称从 attachRole 更改为其他名称并且函数更长。op 值可能与函数名称不同。无论如何,这似乎满足了跟踪问题的一般需要,同时将错误视为一等公民。


Go2 可能会在这方面投入更多的 Java-ish 响应。


查看完整回答
反对 回复 2023-06-01
?
忽然笑

TA贡献1806条经验 获得超5个赞

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


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


查看完整回答
反对 回复 2023-06-01
  • 3 回答
  • 0 关注
  • 421 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信