2014-11-02 21:31:15 +03:00
|
|
|
package logrus_sentry
|
2014-11-01 21:55:40 +03:00
|
|
|
|
|
|
|
import (
|
2014-11-06 23:17:29 +03:00
|
|
|
"fmt"
|
2015-05-22 15:14:51 +03:00
|
|
|
"net/http"
|
2015-07-07 19:41:45 +03:00
|
|
|
"time"
|
2014-11-06 23:17:29 +03:00
|
|
|
|
2014-11-01 21:55:40 +03:00
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
"github.com/getsentry/raven-go"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
severityMap = map[logrus.Level]raven.Severity{
|
|
|
|
logrus.DebugLevel: raven.DEBUG,
|
|
|
|
logrus.InfoLevel: raven.INFO,
|
|
|
|
logrus.WarnLevel: raven.WARNING,
|
|
|
|
logrus.ErrorLevel: raven.ERROR,
|
|
|
|
logrus.FatalLevel: raven.FATAL,
|
|
|
|
logrus.PanicLevel: raven.FATAL,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func getAndDel(d logrus.Fields, key string) (string, bool) {
|
|
|
|
var (
|
|
|
|
ok bool
|
|
|
|
v interface{}
|
|
|
|
val string
|
|
|
|
)
|
|
|
|
if v, ok = d[key]; !ok {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
if val, ok = v.(string); !ok {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
delete(d, key)
|
|
|
|
return val, true
|
|
|
|
}
|
|
|
|
|
2015-05-22 15:14:51 +03:00
|
|
|
func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) {
|
|
|
|
var (
|
|
|
|
ok bool
|
|
|
|
v interface{}
|
|
|
|
req *http.Request
|
|
|
|
)
|
|
|
|
if v, ok = d[key]; !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
if req, ok = v.(*http.Request); !ok || req == nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
delete(d, key)
|
|
|
|
return req, true
|
|
|
|
}
|
|
|
|
|
2014-11-01 21:55:40 +03:00
|
|
|
// SentryHook delivers logs to a sentry server.
|
|
|
|
type SentryHook struct {
|
2014-11-25 15:36:08 +03:00
|
|
|
// Timeout sets the time to wait for a delivery error from the sentry server.
|
|
|
|
// If this is set to zero the server will not wait for any response and will
|
|
|
|
// consider the message correctly sent
|
|
|
|
Timeout time.Duration
|
|
|
|
|
2014-11-01 21:55:40 +03:00
|
|
|
client *raven.Client
|
|
|
|
levels []logrus.Level
|
|
|
|
}
|
|
|
|
|
2014-11-06 12:07:41 +03:00
|
|
|
// NewSentryHook creates a hook to be added to an instance of logger
|
|
|
|
// and initializes the raven client.
|
2014-11-25 15:36:08 +03:00
|
|
|
// This method sets the timeout to 100 milliseconds.
|
2014-11-01 21:55:40 +03:00
|
|
|
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
|
2015-07-07 19:41:45 +03:00
|
|
|
client, err := raven.New(DSN)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &SentryHook{100 * time.Millisecond, client, levels}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewWithTagsSentryHook creates a hook with tags to be added to an instance
|
|
|
|
// of logger and initializes the raven client. This method sets the timeout to
|
|
|
|
// 100 milliseconds.
|
|
|
|
func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
|
|
|
|
client, err := raven.NewWithTags(DSN, tags)
|
2014-11-01 21:55:40 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-11-25 15:36:08 +03:00
|
|
|
return &SentryHook{100 * time.Millisecond, client, levels}, nil
|
2014-11-01 21:55:40 +03:00
|
|
|
}
|
|
|
|
|
2015-07-09 19:05:47 +03:00
|
|
|
// NewWithClientSentryHook creates a hook using an initialized raven client.
|
|
|
|
// This method sets the timeout to 100 milliseconds.
|
|
|
|
func NewWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) {
|
|
|
|
return &SentryHook{100 * time.Millisecond, client, levels}, nil
|
|
|
|
}
|
|
|
|
|
2014-11-01 21:55:40 +03:00
|
|
|
// Called when an event should be sent to sentry
|
|
|
|
// Special fields that sentry uses to give more information to the server
|
|
|
|
// are extracted from entry.Data (if they are found)
|
2015-05-22 15:14:51 +03:00
|
|
|
// These fields are: logger, server_name and http_request
|
2014-11-01 21:55:40 +03:00
|
|
|
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
|
|
|
|
packet := &raven.Packet{
|
|
|
|
Message: entry.Message,
|
|
|
|
Timestamp: raven.Timestamp(entry.Time),
|
|
|
|
Level: severityMap[entry.Level],
|
|
|
|
Platform: "go",
|
|
|
|
}
|
|
|
|
|
|
|
|
d := entry.Data
|
|
|
|
|
|
|
|
if logger, ok := getAndDel(d, "logger"); ok {
|
|
|
|
packet.Logger = logger
|
|
|
|
}
|
|
|
|
if serverName, ok := getAndDel(d, "server_name"); ok {
|
|
|
|
packet.ServerName = serverName
|
|
|
|
}
|
2015-05-22 15:14:51 +03:00
|
|
|
if req, ok := getAndDelRequest(d, "http_request"); ok {
|
|
|
|
packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req))
|
|
|
|
}
|
2014-11-01 21:55:40 +03:00
|
|
|
packet.Extra = map[string]interface{}(d)
|
|
|
|
|
2014-11-06 23:17:29 +03:00
|
|
|
_, errCh := hook.client.Capture(packet, nil)
|
2014-11-25 15:36:08 +03:00
|
|
|
timeout := hook.Timeout
|
|
|
|
if timeout != 0 {
|
|
|
|
timeoutCh := time.After(timeout)
|
|
|
|
select {
|
|
|
|
case err := <-errCh:
|
|
|
|
return err
|
|
|
|
case <-timeoutCh:
|
|
|
|
return fmt.Errorf("no response from sentry server in %s", timeout)
|
|
|
|
}
|
2014-11-06 23:17:29 +03:00
|
|
|
}
|
2014-11-06 12:07:41 +03:00
|
|
|
return nil
|
2014-11-01 21:55:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Levels returns the available logging levels.
|
|
|
|
func (hook *SentryHook) Levels() []logrus.Level {
|
|
|
|
return hook.levels
|
|
|
|
}
|