sentry

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2024 License: Apache-2.0 Imports: 14 Imported by: 0

README

OpenMFP Sentry

The sentry package implements some helper functions to use in applications that want to send error captures to Sentry.

Initialization

Init the Sentry connection at the start of our application e.g. in your main function. You need to provide a valid context that is canceled when your application stops.

ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGKILL, syscall.SIGINT)
defer cancel()

err := sentry.Start(ctx, "sentryDSN", "env", "region", "image", "image tag")
if err != nil {
    // handle error
}

The underlying Sentry SDK then runs in the background and flushes error capturings to Sentry.

Capture Errors

Use the CaptureError function to send errors to Sentry. You have to provide the error and you can add tags and extra information. Tags are string/string value pairs that help filter errors in Sentry. Extras can be any data types with a string as key that are then displayed inside Sentry's error details.

sentry.CaptureError(err, tags, extras)

Tags and Extras are helper data types defined in the Sentry package. Both have an Add method that can be used to easily add new values like so:

extras := sentry.Extras{}
tags := sentry.Tags{}

extras.Add("query", oc.RawQuery)
tags.Add("path", path.String())
Sentry Error

The Sentry package contains an Error type that wraps the original Go error and can be used to distinguish between errors that should be sent to Sentry and those that should not be sent.

err := errors.New("test error")

// wrap any error to mark it an Sentry worthy error  
sentryError := SentryError(err)

if IsSentryError(err) {
	// send it to Sentry
}

The idea behind this is, that if you collect errors in a central place in an application you can wrap errors as sentry.Error to check if it should be sent to Sentry later. Wrapped errors using fmt.Errorf are also supported, so you can wrap sentry.Error errors like so:

err := errors.New("test error")
 
sentryError := SentryError(err)

// create a wrapped error
newErr := fmt.Errorf("added a new error: %w", sentryError)

sentryErr, ok := AsSentryError(newErr)
if ok {
	// sentryErr is loaded from the stack of all errors in the chain
}

Important:

sentry.CaptureError captures errors regardless if it is a sentry.Error or not, mainly for compatibility reasons. But it uses provided tags and extras if the error is of sentry.Error type.

Better Stack Traces

If you create a SentryError from an existing error the current stack trace is added. This is done by wrapping it as an ErrorWithStack from github.com/openmfp/golang-commons/errors

You can use the github.com/openmfp/golang-commons/errors package as a drop-in replacement for the stdlib errors package everywhere in your application. This way you get additional stack traces for every wrapped error in the Sentry UI.

GraphQL Error Presenter

The package contains a function that returns an error presenter that can be used with the github.com/99designs/gqlgen/graphql stack. It can be used in a GraphQL service like so:

import (
    "github.com/openmfp/golang-commons/sentry"
)

gqHandler.SetErrorPresenter(sentry.GraphQLErrorPresenter())

The error presenter enriches the error sent to Sentry with all available information from the GraphQL query. In addition, it only sends error that are wrapped as sentry.Error. If needed, one or more tenant IDs that should be skipped when sending errors (e.g. E2E tenant) can be provided as arguments.

Recover panics

There are rare circumstances where a Go program can crash with a panic. This happens if a nil pointer is de-referended or a not existent map index is accessed. In order to send these errors to Sentry and log them there is a Recover func in the Sentry package. This function can be used in main() to record all panics (but not recover from them). It is also possible to use it in functions that are likely to panic (and then recover without crashing). However, if the Recover() function is used in main() only, it prevents not from crashing, it just logs the panic before crashing.

Please note that the Recover function has to be called with the defer keyword like so:

package main
import (
	"context"
	"os/signal"
	"syscall"
	
    "github.com/openmfp/golang-commons/sentry"
    "github.com/openmfp/golang-commons/logger"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGKILL, syscall.SIGINT)
    defer cancel()
	
    defer sentry.Recover(logger.StdLogger)

    err := sentry.Start(ctx, "sentryDSN", "env", "region", "image", "image tag")
    if err != nil {
        // ...
    }
    
	// ...
}

It is important to notice that recovering in main() does not work for subsequent Go routines. This means you must use a dedicated defer Recover function call for every Go routine. The same holds true for usage of the http server package as it spawns Go routines and comes with its own recover handling. To circumvent this there is a HTTP middleware Recoverer() in this Sentry package.

	router := chi.NewRouter()
	router.Use(logger.StoreLoggerMiddleware(log))
	router.Use(sentry.Recoverer)

For operators, a good place to use the Recover() would be the reconciler function. In this case, if there is any panic in the reconile process it it is logged and the application can recover form it and does not crash.

There is an additional function that can be used as a GraphQL middleware to catch panics and handle them. In your service use it like this:

    gqHandler := handler.NewDefaultServer(graphql.NewExecutableSchema(gql))
    gqHandler.SetRecoverFunc(sentry.GraphQLRecover(log))

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CaptureError

func CaptureError(err error, tags Tags, extras ...Extras)

CaptureError sends an error to Sentry with provided tags and extras

func CaptureSentryError

func CaptureSentryError(err error, tags Tags, extras ...Extras)

CaptureSentryError is a small wrapper that only captures Sentry errors

func ContextWithSentryTags

func ContextWithSentryTags(ctx context.Context, sentryTags map[string]string) context.Context

func GetSentryTagsFromContext

func GetSentryTagsFromContext(ctx context.Context) map[string]string

func GraphQLErrorPresenter

func GraphQLErrorPresenter(skipTenants ...string) graphql.ErrorPresenterFunc

GraphQLErrorPresenter returns a function that can be used as GraphQL error presenter

func GraphQLRecover

func GraphQLRecover(log *logger.Logger) graphql.RecoverFunc

GraphQLRecover returns a function that can be used as GraphQL error presenter

func IsSentryError

func IsSentryError(err error) bool

IsSentryError checks if a given error is of Error type and therefor should be sent to Sentry

func ShouldBeProcessed

func ShouldBeProcessed(err error) bool

func Start

func Start(ctx context.Context, dsn, env, region, name, tag string) error

Start initializes Sentry and flushes errors when the provides context is finished

Types

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error wraps the stdlib error to make it possible to check if an error should be sent to Sentry

func AsSentryError

func AsSentryError(err error) (*Error, bool)

AsSentryError checks if a given error is of Error type or contains a wrapped error and returns it

func (*Error) AddExtra

func (e *Error) AddExtra(key string, value interface{})

AddExtra adds extra data to be sent to Sentry

func (*Error) AddTag

func (e *Error) AddTag(key, value string)

AddTag adds a tag to be sent to Sentry

func (Error) GetExtras

func (e Error) GetExtras() Extras

func (Error) GetReason

func (e Error) GetReason() error

func (Error) GetTags

func (e Error) GetTags() Tags

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Extras

type Extras map[string]interface{}

func (Extras) Add

func (e Extras) Add(key string, value interface{})

Add adds a new extra data field

type SentryErrors

type SentryErrors interface {
	error
	AddExtra(key string, value interface{})
	AddTag(key, value string)
	GetTags() Tags
	GetExtras() Extras
	Unwrap() error
}

SentryErrors defines functions that a SentryError should provide

func SentryError

func SentryError(err error) SentryErrors

SentryError creates a new Error from an original error

type Tags

type Tags map[string]string

func (Tags) Add

func (t Tags) Add(key, value string)

Add adds a new tag

Jump to

Keyboard shortcuts

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