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 为什么使用javacv转封装rtsp为rtmp时出现如下问题:[h264 @ 000000004faf7500]no frame?
  • ¥15 乘性高斯噪声在深度学习网络中的应用
  • ¥15 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛