Newer
Older
"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"`
TagNames []string `long:"tag-names" env:"TAG_NAMES" ini-name:"tag-names" description:"log properties that should be set as tags on the sentry event"` //nolint:lll
// 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, &sentryOptions}
// OnShutdown(func() {
}
// SentryLogger ...
type SentryLogger struct {
hub *sentry.Hub
next io.Writer
options *SentryOptions
}
var (
levelMarker = []byte(`"level":"`)
levelMarkerLen = len(levelMarker)
levelsFirstLetter = []byte(`wefp`)
)
func buildEventIfLevelGtWarn(tagNames []string, p []byte) *sentry.Event {
if i := bytes.Index(p, levelMarker); i != -1 {
first := p[i+levelMarkerLen]
if bytes.IndexByte(levelsFirstLetter, first) != -1 {
func buildEvent(tagNames []string, 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()
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:
for _, tag := range tagNames {
if tag == field {
rawvalue := iter.Read()
var value string
if s, ok := rawvalue.(string); ok {
value = s
} else {
value = fmt.Sprint(rawvalue)
}
if len(value) > 200 {
value = value[:200]
}
event.Tags[tag] = value
return true
}) {
return nil
}
if op, ok := event.Extra["api-operation-id"]; ok {
if strOp, ok := op.(string); ok {
event.Message = MustExtraGetStringD(event.Extra, "api-tag", "") + "/" + strOp + ": " + event.Message
} else {
panic("operation is not a string")
}
} else if event.Request != nil && event.Request.Method != "" {
event.Message = event.Request.Method + " " + event.Request.URL + ": " + event.Message
}
func MustExtraGetStringD(extra map[string]interface{}, key, defaultVal string) string {
str, err := ExtraGetStringD(extra, key, defaultVal)
if err != nil {
panic(err)
}
return str
}
func ExtraGetStringD(extra map[string]interface{}, key, defaultVal string) (string, error) {
if value, ok := extra[key]; ok {
if strValue, ok := value.(string); ok {
return strValue, nil
}
return "", fmt.Errorf("error %w: value %#v", ErrNotAString, value)
}
return defaultVal, nil
}
if event := buildEventIfLevelGtWarn(l.options.TagNames, p); event != nil {