-
Christophe de Vienne authoredChristophe de Vienne authored
sentry.go 2.97 KiB
package orusapi
import (
"bytes"
"io"
"github.com/getsentry/sentry-go"
jsoniter "github.com/json-iterator/go"
)
// SentryOptions ...
type SentryOptions struct {
SentryDSN func(string) `long:"dsn" env:"DSN" ini-name:"dsn" description:"Sentry DSN"`
}
// NewSentryOptions ...
func NewSentryOptions(loggingOptions *LoggingOptions) *SentryOptions {
sentryOptions := SentryOptions{}
sentryOptions.SentryDSN = func(dsn string) {
log := loggingOptions.Logger()
client, err := sentry.NewClient(sentry.ClientOptions{
Dsn: dsn,
})
if err != nil {
log.Err(err).Msg("Could not initialize sentry")
return
}
hub := sentry.NewHub(client, sentry.NewScope())
loggingOptions.AddLogWrapper(
func(next io.Writer) io.Writer {
return SentryLogger{hub, next}
})
//OnShutdown(func() {
//hub.Flush(time.Second)
//})
}
return &sentryOptions
}
// SentryLogger ...
type SentryLogger struct {
hub *sentry.Hub
next io.Writer
}
var (
levelMarker = []byte(`"level":"`)
levelMarkerLen = len(levelMarker)
levelsFirstLetter = []byte(`wefp`)
)
func buildEventIfLevelGtWarn(p []byte) *sentry.Event {
if i := bytes.Index(p, levelMarker); i != -1 {
first := p[i+levelMarkerLen]
if bytes.IndexByte(levelsFirstLetter, first) != -1 {
return buildEvent(p)
}
}
return nil
}
func buildEvent(p []byte) *sentry.Event {
iter := jsoniter.ConfigFastest.BorrowIterator(p)
defer jsoniter.ConfigFastest.ReturnIterator(iter)
if iter.WhatIsNext() != jsoniter.ObjectValue {
return nil
}
event := sentry.NewEvent()
if !iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
switch field {
case "message":
event.Message = iter.ReadString()
case "error":
errMsg := iter.ReadString()
if event.Message == "" {
event.Message = errMsg
}
event.Extra["error"] = errMsg
case "level":
level := iter.ReadString()
if level == "warn" {
event.Level = sentry.LevelWarning
} else {
event.Level = sentry.Level(level)
}
case "request":
iter.ReadVal(&event.Request)
case "user":
if !iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
switch field {
case "id":
event.User.ID = iter.ReadString()
default:
event.Extra["user."+field] = iter.Read()
}
return true
}) {
return false
}
case "exception":
var e sentry.Exception
iter.ReadVal(&e)
event.Exception = []sentry.Exception{e}
default:
event.Extra[field] = iter.Read()
}
return true
}) {
return nil
}
if op, ok := event.Extra["api-operation-id"]; ok {
tag, _ := event.Extra["api-tag"].(string)
event.Message = tag + "/" + op.(string) + ": " + event.Message
} else if event.Request != nil && event.Request.Method != "" {
event.Message = event.Request.Method + " " + event.Request.URL + ": " + event.Message
}
return event
}
// Write ...
func (l SentryLogger) Write(p []byte) (n int, err error) {
if event := buildEventIfLevelGtWarn(p); event != nil {
l.hub.CaptureEvent(event)
}
return l.next.Write(p)
}