zapsentry

package module
v1.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 4, 2024 License: MIT Imports: 8 Imported by: 55

README

Sentry client for zap logger

Integration using Sentry Client

Integration of sentry client into zap.Logger is pretty simple:

func modifyToSentryLogger(log *zap.Logger, client *sentry.Client) *zap.Logger {
	cfg := zapsentry.Configuration{
		Level: zapcore.ErrorLevel, //when to send message to sentry
		EnableBreadcrumbs: true, // enable sending breadcrumbs to Sentry 
		BreadcrumbLevel: zapcore.InfoLevel, // at what level should we sent breadcrumbs to sentry, this level can't be higher than `Level`
		Tags: map[string]string{
			"component": "system",
		},
	}
	core, err := zapsentry.NewCore(cfg, zapsentry.NewSentryClientFromClient(client))
	
	// don't use value if error was returned. Noop core will be replaced to nil soon.
	if err != nil {
		panic(err)
	}
	
	log = zapsentry.AttachCoreToLogger(core, log)

	// if you have web service, create a new scope somewhere in middleware to have valid breadcrumbs.
	return log.With(zapsentry.NewScope())
}

Please note that wrapper does not guarantee that all your events will be sent before the app exits. Flush called internally only in case of writing message with severity level > zapcore.ErrorLevel (i.e. Fatal, Panic, ...). If you want to ensure your messages come to sentry - call the flush on native sentry client at defer. Example:

func main() {
	sentryClient, err := sentry.NewClient(sentry.ClientOptions{
		Dsn: "Sentry DSN",
	})
	if err != nil {
		// Handle the error here
	}
	// Flush buffered events before the program terminates.
	// Set the timeout to the maximum duration the program can afford to wait.
	defer sentryClient.Flush(2 * time.Second)
	
	// create zap log and wrapper...
	
	// create and run your app here...
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidBreadcrumbLevel = errors.New("breadcrumb level must be lower than or equal to error level")
)

Functions

func AttachCoreToLogger

func AttachCoreToLogger(sentryCore zapcore.Core, l *zap.Logger) *zap.Logger
Example
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/getsentry/sentry-go"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/zap/zaptest/observer"

	"github.com/TheZeroSlave/zapsentry"
)

func main() {
	// Setup zap with observer (for testing), originally we use
	// 		config = zap.NewDevelopmentConfig()
	//  	logger, err := config.Build()
	// to build zap logger, here we use zap/zaptest/observer for testing
	core, recordedLogs := observer.New(zapcore.DebugLevel)
	logger := zap.New(core, zap.AddStacktrace(zap.DebugLevel))

	// Setup mock sentry client for testing, in general we use sentry.NewClient
	var recordedSentryEvent *sentry.Event
	sentryClient := mockSentryClient(func(event *sentry.Event) {
		recordedSentryEvent = event
	})

	// Setup zapsentry
	core, err := zapsentry.NewCore(zapsentry.Configuration{
		Level:             zapcore.ErrorLevel, // when to send message to sentry
		EnableBreadcrumbs: true,               // enable sending breadcrumbs to Sentry
		BreadcrumbLevel:   zapcore.InfoLevel,  // at what level should we sent breadcrumbs to sentry
		Tags: map[string]string{
			"component": "system",
		},
		FrameMatcher: zapsentry.CombineFrameMatchers(
			// skip all frames having a prefix of 'go.uber.org/zap'
			// this can be used to exclude e.g. logging adapters from the stacktrace
			zapsentry.SkipFunctionPrefixFrameMatcher("go.uber.org/zap"),
		),
	}, zapsentry.NewSentryClientFromClient(sentryClient))
	if err != nil {
		log.Fatal(err)
	}
	newLogger := zapsentry.AttachCoreToLogger(core, logger)

	// Send error log
	newLogger.
		With(zapsentry.NewScope()).
		Error("[error] something went wrong!", zap.String("method", "unknown"), zapsentry.Tag("service", "app"))

	// Check output
	fmt.Println(recordedLogs.All()[0].Message)
	fmt.Println(recordedSentryEvent.Message)
	fmt.Println(recordedSentryEvent.Extra)
	fmt.Println(recordedSentryEvent.Tags)
}

func mockSentryClient(f func(event *sentry.Event)) *sentry.Client {
	client, _ := sentry.NewClient(sentry.ClientOptions{
		Dsn:       "",
		Transport: &transport{MockSendEvent: f},
	})
	return client
}

type transport struct {
	MockSendEvent func(event *sentry.Event)
}

// Flush waits until any buffered events are sent to the Sentry server, blocking
// for at most the given timeout. It returns false if the timeout was reached.
func (f *transport) Flush(_ time.Duration) bool { return true }

// Configure is called by the Client itself, providing it it's own ClientOptions.
func (f *transport) Configure(_ sentry.ClientOptions) {}

// SendEvent assembles a new packet out of Event and sends it to remote server.
// We use this method to capture the event for testing
func (f *transport) SendEvent(event *sentry.Event) {
	f.MockSendEvent(event)
}
Output:

[error] something went wrong!
[error] something went wrong!
map[method:unknown]
map[component:system service:app]

func Context added in v1.18.0

func Context(ctx context.Context) zap.Field

Context adds a context to the logger. This can be used e.g. to pass trace information to sentry and allow linking events to their respective traces.

See also https://docs.sentry.io/platforms/go/performance/instrumentation/opentelemetry/#linking-errors-to-transactions

func NewCore

func NewCore(cfg Configuration, factory SentryClientFactory) (zapcore.Core, error)

func NewScope added in v1.8.0

func NewScope() zapcore.Field

func NewScopeFromScope added in v1.11.0

func NewScopeFromScope(scope *sentry.Scope) zapcore.Field

func Tag added in v1.14.0

func Tag(key string, value string) zap.Field

Types

type ClientGetter added in v1.0.1

type ClientGetter interface {
	GetClient() *sentry.Client
}

type Configuration

type Configuration struct {
	// Tags are passed as is to the corresponding sentry.Event field.
	Tags map[string]string

	// LoggerNameKey is the key for zap logger name.
	// If not empty, the name is added to the rest of zapcore.Field(s),
	// so that be careful with key duplicates.
	// Leave LoggerNameKey empty to disable the feature.
	LoggerNameKey string

	// DisableStacktrace disables adding stacktrace to sentry.Event, if set.
	DisableStacktrace bool

	// Level is the minimal level of sentry.Event(s).
	Level zapcore.LevelEnabler

	// EnableBreadcrumbs enables use of sentry.Breadcrumb(s).
	// This feature works only when you explicitly passed new scope.
	EnableBreadcrumbs bool

	// BreadcrumbLevel is the minimal level of sentry.Breadcrumb(s).
	// Breadcrumb specifies an application event that occurred before a Sentry event.
	// NewCore fails if BreadcrumbLevel is greater than Level.
	// The field is ignored, if EnableBreadcrumbs is not set.
	BreadcrumbLevel zapcore.LevelEnabler

	// MaxBreadcrumbs is the maximum number of breadcrumb events to keep.
	// Leave it zero or set to negative for a reasonable default value.
	// The field is ignored, if EnableBreadcrumbs is not set.
	MaxBreadcrumbs int

	// FlushTimeout is the timeout for flushing events to Sentry.
	FlushTimeout time.Duration

	// Hub overrides the sentry.CurrentHub value.
	// See sentry.Hub docs for more detail.
	Hub *sentry.Hub

	// FrameMatcher allows to ignore some frames of the stack trace.
	// this is particularly useful when you want to ignore for instances frames from convenience wrappers
	FrameMatcher FrameMatcher
}

Configuration is a minimal set of parameters for Sentry integration.

type FrameMatcher added in v1.17.0

type FrameMatcher interface {
	Matches(f sentry.Frame) bool
}

func CombineFrameMatchers added in v1.17.0

func CombineFrameMatchers(matcher ...FrameMatcher) FrameMatcher

type FrameMatcherFunc added in v1.17.0

type FrameMatcherFunc func(f sentry.Frame) bool

func (FrameMatcherFunc) Matches added in v1.17.0

func (f FrameMatcherFunc) Matches(frame sentry.Frame) bool

type FrameMatchers added in v1.17.0

type FrameMatchers []FrameMatcher

func (FrameMatchers) Matches added in v1.17.0

func (ff FrameMatchers) Matches(frame sentry.Frame) bool

type LevelEnabler added in v1.8.0

type LevelEnabler struct {
	zapcore.LevelEnabler
	// contains filtered or unexported fields
}

func (*LevelEnabler) Enabled added in v1.8.0

func (l *LevelEnabler) Enabled(lvl zapcore.Level) bool

type SentryClientFactory

type SentryClientFactory func() (*sentry.Client, error)

func NewSentryClientFromClient

func NewSentryClientFromClient(client *sentry.Client) SentryClientFactory

func NewSentryClientFromDSN

func NewSentryClientFromDSN(DSN string) SentryClientFactory

type SkipFunctionPrefixFrameMatcher added in v1.17.0

type SkipFunctionPrefixFrameMatcher string

func (SkipFunctionPrefixFrameMatcher) Matches added in v1.17.0

func (f SkipFunctionPrefixFrameMatcher) Matches(frame sentry.Frame) bool

type SkipModulePrefixFrameMatcher added in v1.17.0

type SkipModulePrefixFrameMatcher string

func (SkipModulePrefixFrameMatcher) Matches added in v1.17.0

func (f SkipModulePrefixFrameMatcher) Matches(frame sentry.Frame) bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL