7c4d584e58
Signed-off-by: Matt Tescher <matthew.tescher@docker.com>
82 lines
2.4 KiB
Go
82 lines
2.4 KiB
Go
package logrus_bugsnag
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/bugsnag/bugsnag-go"
|
|
bugsnag_errors "github.com/bugsnag/bugsnag-go/errors"
|
|
)
|
|
|
|
type bugsnagHook struct{}
|
|
|
|
// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
|
|
// bugsnag.Configure. Bugsnag must be configured before the hook.
|
|
var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
|
|
|
|
// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
|
|
// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
|
|
// failed.
|
|
type ErrBugsnagSendFailed struct {
|
|
err error
|
|
}
|
|
|
|
func (e ErrBugsnagSendFailed) Error() string {
|
|
return "failed to send error to Bugsnag: " + e.err.Error()
|
|
}
|
|
|
|
// NewBugsnagHook initializes a logrus hook which sends exceptions to an
|
|
// exception-tracking service compatible with the Bugsnag API. Before using
|
|
// this hook, you must call bugsnag.Configure(). The returned object should be
|
|
// registered with a log via `AddHook()`
|
|
//
|
|
// Entries that trigger an Error, Fatal or Panic should now include an "error"
|
|
// field to send to Bugsnag.
|
|
func NewBugsnagHook() (*bugsnagHook, error) {
|
|
if bugsnag.Config.APIKey == "" {
|
|
return nil, ErrBugsnagUnconfigured
|
|
}
|
|
return &bugsnagHook{}, nil
|
|
}
|
|
|
|
// skipStackFrames skips logrus stack frames before logging to Bugsnag.
|
|
const skipStackFrames = 4
|
|
|
|
// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
|
|
// "error" field (or the Message if the error isn't present) and sends it off.
|
|
func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
|
|
var notifyErr error
|
|
err, ok := entry.Data["error"].(error)
|
|
if ok {
|
|
notifyErr = err
|
|
} else {
|
|
notifyErr = errors.New(entry.Message)
|
|
}
|
|
|
|
metadata := bugsnag.MetaData{}
|
|
metadata["metadata"] = make(map[string]interface{})
|
|
for key, val := range entry.Data {
|
|
if key != "error" {
|
|
metadata["metadata"][key] = val
|
|
}
|
|
}
|
|
|
|
errWithStack := bugsnag_errors.New(notifyErr, skipStackFrames)
|
|
bugsnagErr := bugsnag.Notify(errWithStack, metadata)
|
|
if bugsnagErr != nil {
|
|
return ErrBugsnagSendFailed{bugsnagErr}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Levels enumerates the log levels on which the error should be forwarded to
|
|
// bugsnag: everything at or above the "Error" level.
|
|
func (hook *bugsnagHook) Levels() []logrus.Level {
|
|
return []logrus.Level{
|
|
logrus.ErrorLevel,
|
|
logrus.FatalLevel,
|
|
logrus.PanicLevel,
|
|
}
|
|
}
|