hooks: Add BugSnag hook

This commit is contained in:
Burke Libbey 2015-03-16 15:29:39 -04:00
parent 2cea0f0d14
commit 83752ed3c5
No known key found for this signature in database
GPG Key ID: E893DEF914F22410
2 changed files with 117 additions and 0 deletions

53
hooks/bugsnag/bugsnag.go Normal file
View File

@ -0,0 +1,53 @@
package logrus_bugsnag
import (
"github.com/Sirupsen/logrus"
"github.com/bugsnag/bugsnag-go"
)
// BugsnagHook sends exceptions to an exception-tracking service compatible
// with the Bugsnag API. Before using this hook, you must call
// bugsnag.Configure().
//
// Entries that trigger an Error, Fatal or Panic should now include an "error"
// field to send to Bugsnag
type BugsnagHook struct{}
// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
// implicitly-required "error" field and sends it off.
func (hook *BugsnagHook) Fire(entry *logrus.Entry) error {
if entry.Data["error"] == nil {
entry.Logger.WithFields(logrus.Fields{
"source": "bugsnag",
}).Warn("Exceptions sent to Bugsnag must have an 'error' key with the error")
return nil
}
err, ok := entry.Data["error"].(error)
if !ok {
entry.Logger.WithFields(logrus.Fields{
"source": "bugsnag",
}).Warn("Exceptions sent to Bugsnag must have an `error` key of type `error`")
return nil
}
bugsnagErr := bugsnag.Notify(err)
if bugsnagErr != nil {
entry.Logger.WithFields(logrus.Fields{
"source": "bugsnag",
"error": bugsnagErr,
}).Warn("Failed to send error to Bugsnag")
}
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,
}
}

View File

@ -0,0 +1,64 @@
package logrus_bugsnag
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/Sirupsen/logrus"
"github.com/bugsnag/bugsnag-go"
)
type notice struct {
Events []struct {
Exceptions []struct {
Message string `json:"message"`
} `json:"exceptions"`
} `json:"events"`
}
func TestNoticeReceived(t *testing.T) {
msg := make(chan string, 1)
expectedMsg := "foo"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var notice notice
data, _ := ioutil.ReadAll(r.Body)
if err := json.Unmarshal(data, &notice); err != nil {
t.Error(err)
}
_ = r.Body.Close()
msg <- notice.Events[0].Exceptions[0].Message
}))
defer ts.Close()
hook := &BugsnagHook{}
bugsnag.Configure(bugsnag.Configuration{
Endpoint: ts.URL,
ReleaseStage: "production",
APIKey: "12345678901234567890123456789012",
Synchronous: true,
})
log := logrus.New()
log.Hooks.Add(hook)
log.WithFields(logrus.Fields{
"error": errors.New(expectedMsg),
}).Error("Bugsnag will not see this string")
select {
case received := <-msg:
if received != expectedMsg {
t.Errorf("Unexpected message received: %s", received)
}
case <-time.After(time.Second):
t.Error("Timed out; no notice received by Bugsnag API")
}
}