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

如何获取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?

  • 写回答

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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?