3 回答

TA貢獻(xiàn)1816條經(jīng)驗(yàn) 獲得超4個贊
(順便說一句,似乎來自與 Logrus 沒有任何關(guān)系且對 Logrus 沒有做出任何貢獻(xiàn)的人,因此實(shí)際上不是來自“Logrus 團(tuán)隊(duì)”)。
如記錄的那樣pkg/errors,很容易在錯誤中提取堆棧跟蹤:
type stackTracer interface {
? ? ? ? StackTrace() errors.StackTrace
}
這意味著使用 logrus 記錄堆棧跟蹤的最簡單方法就是:
if stackErr, ok := err.(stackTracer); ok {
? ? log.WithField("stacktrace", fmt.Sprintf("%+v", stackErr.StackTrace()))
}
從今天開始,當(dāng)我的一個拉取請求與 合并pkg/errors時,如果您使用的是 JSON 日志記錄,現(xiàn)在就更容易了:
if stackErr, ok := err.(stackTracer); ok {
? ? log.WithField("stacktrace", stackErr.StackTrace())
}
這將生成類似于“%+v”的日志格式,但沒有換行符或制表符,每個字符串有一個日志條目,以便于編組到 JSON 數(shù)組中。
當(dāng)然,這兩個選項(xiàng)都會強(qiáng)制您使用 定義的格式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
? ? ? ? }
}
您可以將其強(qiáng)制轉(zhuǎn)換為您喜歡的任何格式,而不是打印每一幀。

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超2個贊
推論是錯誤的。Logrus 實(shí)際上并不知道如何處理錯誤。?
類似 Java 的響應(yīng)?為了以這種方式普遍使用錯誤處理程序,我編寫了一個新版本的 Entry,它來自 Logrus。如示例所示,使用您想要的任何常用字段創(chuàng)建一個新條目(示例下方是在處理程序中設(shè)置的記錄器,用于跟蹤調(diào)用者 ID。在處理?xiàng)l目時通過層傳遞 PgkError。當(dāng)您需要時記錄特定的錯誤,比如遇到錯誤的調(diào)用變量,從 PkgError.WithError(...) 開始,然后添加您的詳細(xì)信息。
這是一個起點(diǎn)。如果您想普遍使用它,請?jiān)?PkgErrorEntry 上實(shí)現(xiàn)所有 Entity 接口。繼續(xù)委托給內(nèi)部入口,但返回一個新的 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寫過關(guān)于讓錯誤成為你領(lǐng)域的一部分。一個簡化版本是您應(yīng)該將跟蹤器屬性放在自定義錯誤上。當(dāng)直接受您控制的代碼出錯或發(fā)生來自第 3 方庫的錯誤時,立即處理錯誤的代碼應(yīng)將唯一值放入自定義錯誤中。該值將作為自定義錯誤Error() string實(shí)現(xiàn)的一部分打印。
當(dāng)開發(fā)人員獲得日志文件時,他們將能夠 grep 代碼庫以獲取該唯一值。Ben 說:“最后,我們需要能夠向我們的操作員提供所有這些信息以及邏輯堆棧跟蹤,以便他們可以調(diào)試問題。Go 已經(jīng)提供了一個簡單的方法 error.Error() 來打印錯誤信息,因此我們可以利用那?!?/p>
這是本的例子
// 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 代碼的一個問題是值很容易偏離原始上下文。例如,假設(shè)函數(shù)的名稱從 attachRole 更改為其他名稱并且函數(shù)更長。op 值可能與函數(shù)名稱不同。無論如何,這似乎滿足了跟蹤問題的一般需要,同時將錯誤視為一等公民。
Go2 可能會在這方面投入更多的 Java-ish 響應(yīng)。

TA貢獻(xiàn)1806條經(jīng)驗(yàn) 獲得超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
- 3 回答
- 0 關(guān)注
- 530 瀏覽
添加回答
舉報(bào)