dongxia2068
2019-01-25 16:31
浏览 493
已采纳

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

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.

From this issue, https://github.com/sirupsen/logrus/issues/506, I infer that logrus has some native method for working with pkg/errors.

How can I do this?

图片转代码服务由CSDN问答提供 功能建议

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

从本期开始, https://github.com/sirupsen/logrus/issues/506 ,我推断logrus有一些处理pkg / errors的本地方法。

我该如何 这样吗?

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • duanpi2033 2019-01-25 16:31
    已采纳

    The inference is wrong. Logrus does not actually know how to handle the error. Update the Logrus team official said that this is NOT a supported feature, https://github.com/sirupsen/logrus/issues/895#issuecomment-457656556.

    A Java-ish Response In order to universally work with error handlers in this way, I composed a new version of Entry, which is from Logrus. As the example shows, create a new Entry with what ever common fields you want (below the example is a logger set in a handler that keeps track of the caller id. Pass PgkError through your layers as you work the Entry. When you need to log specific errors, like call variables experiencing the error, start with the PkgError.WithError(...) then add your details.

    This is a starting point. If you want to use this generally, implement all of the Entity interface on PkgErrorEntry. Continue to delegate to the internal entry, but return a new PkgErrorEntry. Such a change would make the value true drop in replacement for 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, "
    ")
            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")
    }
    

    A Gopher-ish way See https://www.reddit.com/r/golang/comments/ajby88/how_to_get_stack_traces_in_logrus/ for more detail.

    Ben Johnson wrote about making errors part of your domain. An abbreviated version is that you should put tracer attributes onto a custom error. When code directly under your control errors or when an error from a 3rd party library occurs, the code immediately dealing with the error should put a unique value into the custom error. This value will print as part of the custom error's Error() string implementation.

    When developers get the log file, they will be able to grep the code base for that unique value. Ben says "Finally, we need to be able to provide all this information plus a logical stack trace to our operator so they can debug issues. Go already provides a simple method, error.Error(), to print error information so we can utilize that."

    Here's Ben's example

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

    An issue I have with the grep-able code is that it's easy for the value to diverge from the original context. For example, say the name of the function was changed from attachRole to something else and the function was longer. It possible that the op value can diverge from the function name. Regardless, this appears to satisfy the general need of tracing to a problem, while treating errors a first class citizens.

    Go2 might throw a curve at this into more the Java-ish response. Stay tuned. https://go.googlesource.com/proposal/+/refs/changes/97/159497/3/design/XXXXX-error-values.md

    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • doubi1910 2019-01-27 10:47

    The comment on your Logrus issue is incorrect (and incidentally, appears to come from someone with no affiliation with Logrus, and who has made no contributions to Logrus, so not actually from "the Logrus team").

    It is easy to extract the stack trace in a pkg/errors error, as documented:

    type stackTracer interface {
            StackTrace() errors.StackTrace
    }
    

    This means that the easiest way to log the stack trace with logrus would be simply:

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

    As of today, when my a pull request of mine was merged with pkg/errors, this is now even easier, if you're using JSON logging:

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

    This will produce a log format similar to "%+v", but without newlines or tabs, with one log entry per string, for easy marshaling into a JSON array.

    Of course, both of these options force you to use the format defined by pkg/errors, which isn't always ideal. So instead, you can iterate through the stack trace, and produce your own formatting, possibly producing a format easily marshalable to JSON.

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

    Rather than printing each frame, you can coerce it into any format you like.

    评论
    解决 1 无用
    打赏 举报

相关推荐 更多相似问题