From 15e12fe3bd08cced574f7b15827aac9624fde955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyadv=C3=A1ri=20P=C3=A9ter?= Date: Tue, 13 Mar 2018 16:25:23 +0100 Subject: [PATCH 1/5] Use error field for fingerprint Add fields to tag mapping Reformat with fmt --- output.go | 139 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 42 deletions(-) diff --git a/output.go b/output.go index 7481a36..99e5d98 100644 --- a/output.go +++ b/output.go @@ -2,15 +2,23 @@ package xlogsentry import ( "fmt" - "time" "net/http" - "runtime" "os" + "runtime" + "time" "github.com/getsentry/raven-go" - "github.com/rs/xlog" + "github.com/Ak-Army/xlog" ) +type locationer interface { + Location() (string, int) +} + +type causer interface { + Cause() error +} + var ( xlogSeverityMap = map[string]xlog.Level{ "debug": xlog.LevelDebug, @@ -31,38 +39,38 @@ var ( type Output struct { Timeout time.Duration StacktraceConfiguration StackTraceConfiguration + FieldsToTag []string - client *raven.Client - host string + client *raven.Client + host string } // StackTraceConfiguration allows for configuring stacktraces type StackTraceConfiguration struct { // whether stacktraces should be enabled - Enable bool + Enable bool // the level at which to start capturing stacktraces - Level xlog.Level + Level xlog.Level // how many stack frames to skip before stacktrace starts recording - Skip int + Skip int // the number of lines to include around a stack frame for context - Context int + 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 } - -func NewSentryOutput(DSN string, tags map[string]string) *Output { - client, _ := raven.NewClient(DSN, tags) - return newOutput(client) +func NewSentryOutput(DSN string, tags map[string]string, fieldsToTag []string) *Output { + client, _ := raven.NewWithTags(DSN, tags) + return newOutput(client, fieldsToTag) } -func NewSentryOutputWithClient(client *raven.Client) *Output { - return newOutput(client) +func NewSentryOutputWithClient(client *raven.Client, fieldsToTag []string) *Output { + return newOutput(client, fieldsToTag) } -func newOutput(client *raven.Client) *Output { +func newOutput(client *raven.Client, fieldsToTag []string) *Output { hostname, _ := os.Hostname() return &Output{ Timeout: 300 * time.Millisecond, @@ -73,14 +81,15 @@ func newOutput(client *raven.Client) *Output { Context: 0, InAppPrefixes: nil, }, - client: client, - host: hostname, + FieldsToTag: fieldsToTag, + client: client, + host: hostname, } } func getAndDel(fields map[string]interface{}, key string) (string, bool) { var ( - ok bool + ok bool v interface{} val string ) @@ -97,7 +106,7 @@ func getAndDel(fields map[string]interface{}, key string) (string, bool) { func getAndDelRequest(fields map[string]interface{}, key string) (*http.Request, bool) { var ( - ok bool + ok bool v interface{} req *http.Request ) @@ -113,6 +122,25 @@ func getAndDelRequest(fields map[string]interface{}, key string) (*http.Request, // Write implements xlog.Output interface func (o Output) Write(fields map[string]interface{}) error { + packet := o.getPacket(fields) + + _, errCh := o.client.Capture(packet, nil) + + timeout := o.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 +} + +func (o Output) getPacket(fields map[string]interface{}) *raven.Packet { level := xlogSeverityMap[fields[xlog.KeyLevel].(string)] packet := raven.NewPacket(fields[xlog.KeyMessage].(string)) @@ -120,13 +148,26 @@ func (o Output) Write(fields map[string]interface{}) error { packet.Level = severityMap[level] packet.Logger = "xlog" + fields = o.mapFieldsToPacket(fields, packet) + fields = o.addDefaultFields(fields) + fields = o.mapFieldsToTag(fields, packet) + fields = o.cleanFields(fields) - delete(fields, xlog.KeyMessage) - delete(fields, xlog.KeyTime) - delete(fields, xlog.KeyLevel) - delete(fields, xlog.KeyFile) + packet.Extra = fields + + return packet +} +func (o Output) addDefaultFields(fields map[string]interface{}) map[string]interface{} { + fields["runtime.Version"] = runtime.Version() + fields["runtime.NumCPU"] = runtime.NumCPU() + fields["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) + fields["runtime.NumGoroutine"] = runtime.NumGoroutine() + return fields +} +func (o Output) mapFieldsToPacket(fields map[string]interface{}, packet *raven.Packet) map[string]interface{} { + level := xlogSeverityMap[fields[xlog.KeyLevel].(string)] if serverName, ok := getAndDel(fields, "host"); ok { packet.ServerName = serverName } else if serverName, ok := getAndDel(fields, "server_name"); ok { @@ -137,6 +178,21 @@ func (o Output) Write(fields map[string]interface{}) error { if release, ok := getAndDel(fields, "release"); ok { packet.Release = release } + if fingerprint, ok := getAndDel(fields, "fingerprint"); ok { + packet.Fingerprint = append(packet.Fingerprint, fingerprint) + } else { + err := fields[xlog.KeyError] + if err, ok := err.(locationer); ok { + file, line := err.Location() + if file != "" { + packet.Fingerprint = append(packet.Fingerprint, fmt.Sprintf("%s:%d", file, line)) + } + } else if err, ok := err.(causer); ok { + packet.Fingerprint = append(packet.Fingerprint, fmt.Sprint(err.Cause())) + } else { + packet.Fingerprint = append(packet.Fingerprint, fields[xlog.KeyMessage].(string)) + } + } if culprit, ok := getAndDel(fields, "culprit"); ok { packet.Culprit = culprit } else if role, ok := getAndDel(fields, "role"); ok { @@ -146,31 +202,30 @@ func (o Output) Write(fields map[string]interface{}) error { packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) } - fields["runtime.Version"] = runtime.Version() - fields["runtime.NumCPU"] = runtime.NumCPU() - fields["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) - fields["runtime.NumGoroutine"] = runtime.NumGoroutine() - stConfig := o.StacktraceConfiguration if stConfig.Enable && level <= stConfig.Level { currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes) packet.Interfaces = append(packet.Interfaces, currentStacktrace) } + return fields +} - packet.Extra = map[string]interface{}(fields) - - _, errCh := o.client.Capture(packet, nil) - - timeout := o.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) +func (o Output) mapFieldsToTag(fields map[string]interface{}, packet *raven.Packet) map[string]interface{} { + for _, tag := range o.FieldsToTag { + if value, ok := getAndDel(fields, tag); ok { + packet.AddTags(map[string]string{tag: value}) } + } - return nil + return fields +} +func (o Output) cleanFields(fields map[string]interface{}) map[string]interface{} { + delete(fields, xlog.KeyMessage) + delete(fields, xlog.KeyTime) + delete(fields, xlog.KeyLevel) + delete(fields, xlog.KeyFile) + delete(fields, xlog.KeyError) + + return fields } From 9961cd5d63cf219bcf9a4e91ec8ce4d533fc3718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyadv=C3=A1ri=20P=C3=A9ter?= Date: Mon, 19 Mar 2018 19:52:27 +0100 Subject: [PATCH 2/5] use go fork convention --- output.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/output.go b/output.go index 99e5d98..7e6b1c7 100644 --- a/output.go +++ b/output.go @@ -8,7 +8,7 @@ import ( "time" "github.com/getsentry/raven-go" - "github.com/Ak-Army/xlog" + "github.com/rs/xlog" ) type locationer interface { From 0283fd1ce3da376e287bf347acc605764fa22603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyadv=C3=A1ri=20P=C3=A9ter?= Date: Tue, 4 Jun 2019 13:56:15 +0200 Subject: [PATCH 3/5] Do not add stacktrace if its nil --- output.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/output.go b/output.go index 7e6b1c7..7b29c80 100644 --- a/output.go +++ b/output.go @@ -205,7 +205,12 @@ func (o Output) mapFieldsToPacket(fields map[string]interface{}, packet *raven.P stConfig := o.StacktraceConfiguration if stConfig.Enable && level <= stConfig.Level { currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes) - packet.Interfaces = append(packet.Interfaces, currentStacktrace) + if currentStacktrace==nil { + currentStacktrace = raven.NewStacktrace(0, stConfig.Context, stConfig.InAppPrefixes) + } + if currentStacktrace != nil { + packet.Interfaces = append(packet.Interfaces, currentStacktrace) + } } return fields } From 17094f2d9e1046c6a985fe3e66e20ed1e3841fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyadv=C3=A1ri=20P=C3=A9ter?= Date: Thu, 13 Jun 2019 16:15:32 +0200 Subject: [PATCH 4/5] Leave fields unmutated. --- output.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/output.go b/output.go index 7b29c80..47a4e47 100644 --- a/output.go +++ b/output.go @@ -148,12 +148,17 @@ func (o Output) getPacket(fields map[string]interface{}) *raven.Packet { packet.Level = severityMap[level] packet.Logger = "xlog" - fields = o.mapFieldsToPacket(fields, packet) - fields = o.addDefaultFields(fields) - fields = o.mapFieldsToTag(fields, packet) - fields = o.cleanFields(fields) + fieldsCopy := make(map[string]interface{}) + for k, v := range fields { + fieldsCopy[k] = v + } + + fieldsCopy = o.mapFieldsToPacket(fieldsCopy, packet) + fieldsCopy = o.addDefaultFields(fieldsCopy) + fieldsCopy = o.mapFieldsToTag(fieldsCopy, packet) + fieldsCopy = o.cleanFields(fieldsCopy) - packet.Extra = fields + packet.Extra = fieldsCopy return packet } From a717472eb8452c13fe0a3aa4bde58e7f39a4573b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyadv=C3=A1ri=20P=C3=A9ter?= Date: Tue, 25 Jun 2019 16:42:31 +0200 Subject: [PATCH 5/5] Handle fatal log level --- output.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/output.go b/output.go index 47a4e47..0038ee7 100644 --- a/output.go +++ b/output.go @@ -25,6 +25,7 @@ var ( "info": xlog.LevelInfo, "warn": xlog.LevelWarn, "error": xlog.LevelError, + "fatal": xlog.LevelFatal, } severityMap = map[xlog.Level]raven.Severity{ @@ -32,6 +33,7 @@ var ( xlog.LevelInfo: raven.INFO, xlog.LevelWarn: raven.WARNING, xlog.LevelError: raven.ERROR, + xlog.LevelFatal: raven.FATAL, } )