Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
431374b
added sentry logger
gigaroby Nov 1, 2014
64c7937
added tests
gigaroby Nov 2, 2014
e5e13b3
various fixes
gigaroby Nov 6, 2014
df1630a
added delivery timeout
gigaroby Nov 6, 2014
ddd0c2e
timeout is now configurable and documented
gigaroby Nov 25, 2014
260c8ae
fix Second const
xboston Mar 2, 2015
2a3d61f
fix Second const
xboston Mar 2, 2015
9796910
Merge branch 'logstash'
sirupsen Mar 19, 2015
e30ec81
Added special field for *http.Request to Sentry hook
evalphobia May 22, 2015
fa5e53f
Added a new SentryHook initialization func for setting tags.
awonak Jul 7, 2015
438f879
updated readme with usage example
awonak Jul 7, 2015
dae7f82
Allow sentry hook to be created using an initialized raven client
Jul 9, 2015
4885f76
fixed import paths in README.md
gigaroby Oct 6, 2015
e6935c7
Merge pull request #2 from gigaroby/fix-import-paths
evalphobia Oct 7, 2015
47b56ff
Update README to add CI badges
evalphobia Oct 7, 2015
9ae3a97
Merge pull request #3 from evalphobia/feature/add-ci-badges
evalphobia Oct 7, 2015
6fdd969
Add stacktrace functionality
Oct 10, 2015
e42a087
Merge pull request #6 from miloconway/add-stacktrace
evalphobia Oct 15, 2015
7ffe97a
Export stackTraceConfiguration struct type
stkao05 Dec 9, 2015
b050b24
Merge pull request #7 from stkao05/config
evalphobia Dec 15, 2015
e1a7e30
Add special sentry field support.
stkao05 Dec 17, 2015
2ec46f6
Merge pull request #8 from stkao05/master
evalphobia Jan 14, 2016
65f2b21
Fixed go ver error
evalphobia Jan 14, 2016
8278333
Add .travis.yml
evalphobia Feb 23, 2016
d265708
Merge pull request #10 from evalphobia/feature/travisci
evalphobia Feb 23, 2016
339ff6c
format extra data before send to sentry (#11)
favadi Apr 8, 2016
8bf132a
Send errors to sentry as an Exception (#12)
belak Jul 5, 2016
49b8cd7
Merge remote-tracking branch 'logrus_sentry/master' into logrus-hook
mzuneska Aug 9, 2016
27495b6
Rename sentry*.go to logrus*.go
mzuneska Aug 9, 2016
38173f9
Move logrus_sentry to raven package
mzuneska Aug 9, 2016
a3d4e75
Rename uuid() to createUUID()
mzuneska Aug 9, 2016
3700aa5
Fix tests
mzuneska Aug 9, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (packet *Packet) Init(project string) error {
}
if packet.EventID == "" {
var err error
packet.EventID, err = uuid()
packet.EventID, err = createUUID()
if err != nil {
return err
}
Expand Down Expand Up @@ -219,7 +219,7 @@ func (packet *Packet) AddTags(tags map[string]string) {
}
}

func uuid() (string, error) {
func createUUID() (string, error) {
id := make([]byte, 16)
_, err := io.ReadFull(rand.Reader, id)
if err != nil {
Expand Down
240 changes: 240 additions & 0 deletions logrus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
package raven

import (
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/Sirupsen/logrus"
)

var (
severityMap = map[logrus.Level]Severity{
logrus.DebugLevel: DEBUG,
logrus.InfoLevel: INFO,
logrus.WarnLevel: WARNING,
logrus.ErrorLevel: ERROR,
logrus.FatalLevel: FATAL,
logrus.PanicLevel: FATAL,
}
)

func getEventID(d logrus.Fields) (string, bool) {
eventID, ok := d["event_id"].(string)

if !ok {
return "", false
}

//verify eventID is 32 characters hexadecimal string (UUID4)
uuid := parseUUID(eventID)

if uuid == nil {
return "", false
}

return uuid.noDashString(), true
}

func getUserContext(d logrus.Fields) (*User, bool) {
if v, ok := d["user"]; ok {
switch val := v.(type) {
case *User:
return val, true

case User:
return &val, true
}
}

username, _ := d["user_name"].(string)
email, _ := d["user_email"].(string)
id, _ := d["user_id"].(string)
ip, _ := d["user_ip"].(string)

if username == "" && email == "" && id == "" && ip == "" {
return nil, false
}

return &User{
ID: id,
Username: username,
Email: email,
IP: ip,
}, true
}

func getAndDel(d logrus.Fields, key string) (string, bool) {
if value, ok := d[key].(string); ok {
delete(d, key)
return value, true
} else {
return "", false
}
}

func getAndDelError(d logrus.Fields, key string) (error, bool) {
if value, ok := d[key].(error); ok {
delete(d, key)
return value, true
} else {
return nil, false
}
}

func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) {
if value, ok := d[key].(*http.Request); ok {
delete(d, key)
return value, true
} else {
return nil, false
}
}

// SentryHook delivers logs to a sentry server.
type SentryHook struct {
// 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
StacktraceConfiguration StackTraceConfiguration

client *Client
levels []logrus.Level
}

// StackTraceConfiguration allows for configuring stacktraces
type StackTraceConfiguration struct {
// whether stacktraces should be enabled
Enable bool
// the level at which to start capturing stacktraces
Level logrus.Level
// how many stack frames to skip before stacktrace starts recording
Skip int
// the number of lines to include around a stack frame for context
Context int
// the prefixes that will be matched against the stack frame.
// if the stack frame's package matches one of these prefixes
// sentry will identify the stack frame as "in_app"
InAppPrefixes []string
}

// NewSentryHook creates a hook to be added to an instance of logger
// and initializes the raven client.
// This method sets the timeout to 100 milliseconds.
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
client, err := New(DSN)
if err != nil {
return nil, err
}
return NewWithClientSentryHook(client, levels)
}

// 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 := NewWithTags(DSN, tags)
if err != nil {
return nil, err
}
return NewWithClientSentryHook(client, levels)
}

// NewWithClientSentryHook creates a hook using an initialized raven client.
// This method sets the timeout to 100 milliseconds.
func NewWithClientSentryHook(client *Client, levels []logrus.Level) (*SentryHook, error) {
return &SentryHook{
Timeout: 100 * time.Millisecond,
StacktraceConfiguration: StackTraceConfiguration{
Enable: false,
Level: logrus.ErrorLevel,
Skip: 5,
Context: 0,
InAppPrefixes: nil,
},
client: client,
levels: levels,
}, nil
}

func formatExtraData(fields logrus.Fields) (ret map[string]interface{}) {
ret = make(map[string]interface{}, len(fields))
for key, value := range fields {
switch value := value.(type) {
case json.Marshaler:
ret[key] = value
case error:
ret[key] = value.Error()
case fmt.Stringer:
ret[key] = value.String()
default:
ret[key] = value
}
}
return
}

// 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)
// These fields are: error, logger, server_name and http_request
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
packet := &Packet{
Message: entry.Message,
Timestamp: 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
}
if req, ok := getAndDelRequest(d, "http_request"); ok {
packet.Interfaces = append(packet.Interfaces, NewHttp(req))
}
if user, ok := getUserContext(d); ok {
packet.Interfaces = append(packet.Interfaces, user)
}
if eventID, ok := getEventID(d); ok {
packet.EventID = eventID
}

stConfig := &hook.StacktraceConfiguration
if stConfig.Enable && entry.Level <= stConfig.Level {
currentStacktrace := NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes)
if err, ok := getAndDelError(d, logrus.ErrorKey); ok {
exc := NewException(err, currentStacktrace)
packet.Interfaces = append(packet.Interfaces, exc)
packet.Culprit = err.Error()
} else {
packet.Interfaces = append(packet.Interfaces, currentStacktrace)
}
}

packet.Extra = formatExtraData(d)

_, errCh := hook.client.Capture(packet, nil)
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)
}
}
return nil
}

// Levels returns the available logging levels.
func (hook *SentryHook) Levels() []logrus.Level {
return hook.levels
}
Loading