package log import ( "go.uber.org/zap/zapcore" "time" ) import "github.com/getsentry/sentry-go" type SentryCoreConfig struct { Tags map[string]string AttachStacktrace bool Level zapcore.Level FlushTimeout time.Duration Hub *sentry.Hub Platform string } type sentryCore struct { zapcore.LevelEnabler client sentry.Client cfg SentryCoreConfig flushTimeout time.Duration fields map[string]interface{} } func (c *sentryCore) with(fs []zapcore.Field) *sentryCore { m := make(map[string]interface{}, len(c.fields)) for k, v := range c.fields { m[k] = v } enc := zapcore.NewMapObjectEncoder() for _, f := range fs { f.AddTo(enc) } for k, v := range enc.Fields { m[k] = v } return &sentryCore{ client: c.client, cfg: c.cfg, fields: m, LevelEnabler: c.LevelEnabler, } } func (c *sentryCore) With(fs []zapcore.Field) zapcore.Core { return c.with(fs) } func (c *sentryCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { if c.cfg.Level.Enabled(ent.Level) { return ce.AddCore(ent, c) } return ce } func (c *sentryCore) Write(ent zapcore.Entry, fs []zapcore.Field) error { clone := c.with(fs) event := sentry.NewEvent() event.Message = ent.Message event.Timestamp = ent.Time event.Level = sentryLevel(ent.Level) event.Platform = c.cfg.Platform event.Extra = clone.fields event.Tags = c.cfg.Tags if c.cfg.AttachStacktrace { trace := sentry.NewStacktrace() if trace != nil { event.Exception = []sentry.Exception{ { Type: ent.Message, Value: ent.Caller.TrimmedPath(), Stacktrace: trace, }, } } } hub := c.cfg.Hub if hub == nil { hub = sentry.CurrentHub() } _ = c.client.CaptureEvent(event, nil, hub.Scope()) if ent.Level > zapcore.ErrorLevel { c.client.Flush(c.flushTimeout) } return nil } func (c *sentryCore) Sync() error { c.client.Flush(c.flushTimeout) return nil } func NewSentryCore(cfg SentryCoreConfig, sentryClient *sentry.Client) zapcore.Core { core := sentryCore{ client: *sentryClient, cfg: cfg, LevelEnabler: cfg.Level, flushTimeout: 3 * time.Second, fields: make(map[string]interface{}), } if cfg.FlushTimeout > 0 { core.flushTimeout = cfg.FlushTimeout } return &core } func sentryLevel(lvl zapcore.Level) sentry.Level { switch lvl { case zapcore.DebugLevel: return sentry.LevelDebug case zapcore.InfoLevel: return sentry.LevelInfo case zapcore.WarnLevel: return sentry.LevelWarning case zapcore.ErrorLevel: return sentry.LevelError case zapcore.DPanicLevel: return sentry.LevelFatal case zapcore.PanicLevel: return sentry.LevelFatal case zapcore.FatalLevel: return sentry.LevelFatal default: return sentry.LevelFatal } }