Files
velero/pkg/util/logging/error_location_hook.go
2017-10-05 08:49:33 -07:00

82 lines
1.6 KiB
Go

package logging
import (
"fmt"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
errorFileField = "error.file"
errorFunctionField = "error.function"
)
// ErrorLocationHook is a logrus hook that attaches error location information
// to log entries if an error is being logged and it has stack-trace information
// (i.e. if it originates from or is wrapped by github.com/pkg/errors)
type ErrorLocationHook struct {
}
func (h *ErrorLocationHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *ErrorLocationHook) Fire(entry *logrus.Entry) error {
var (
errObj interface{}
exists bool
)
if errObj, exists = entry.Data[logrus.ErrorKey]; !exists {
return nil
}
err, ok := errObj.(error)
if !ok {
return errors.New("object logged as error does not satisfy error interface")
}
stackErr := getInnermostTrace(err)
if stackErr != nil {
stackTrace := stackErr.StackTrace()
functionName := fmt.Sprintf("%n", stackTrace[0])
fileAndLine := fmt.Sprintf("%s:%d", stackTrace[0], stackTrace[0])
entry.Data[errorFileField] = fileAndLine
entry.Data[errorFunctionField] = functionName
}
return nil
}
type stackTracer interface {
error
StackTrace() errors.StackTrace
}
type causer interface {
Cause() error
}
// getInnermostTrace returns the innermost error that
// has a stack trace attached
func getInnermostTrace(err error) stackTracer {
var tracer stackTracer
for {
t, isTracer := err.(stackTracer)
if isTracer {
tracer = t
}
c, isCauser := err.(causer)
if isCauser {
err = c.Cause()
} else {
return tracer
}
}
}