Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add BeforeSendTransaction hook #517

Merged
merged 7 commits into from Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Features

- Add `BeforeSendTransaction` hook to `ClientOptions` ([#517](https://github.com/getsentry/sentry-go/pull/517))
- Here's [an example](https://github.com/getsentry/sentry-go/blob/master/_examples/http/main.go#L56-L66) of how BeforeSendTransaction can be used to modify or drop transaction events.

### Bug Fixes

- fix(dynamic-sampling): Do not crash in Span.Finish() when Client is empty [#520](https://github.com/getsentry/sentry-go/pull/520)
Expand Down
13 changes: 12 additions & 1 deletion _examples/http/main.go
Expand Up @@ -48,11 +48,22 @@ func run() error {
// Useful when getting started or trying to figure something out.
Debug: true,
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
// Here you can inspect/modify events before they are sent.
// Here you can inspect/modify non-transaction events (for example, errors) before they are sent.
// Returning nil drops the event.
log.Printf("BeforeSend event [%s]", event.EventID)
return event
},
BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
// Here you can inspect/modify transaction events before they are sent.
// Returning nil drops the event.
if strings.Contains(event.Message, "test-transaction") {
// Drop the transaction
return nil
}
event.Message += " [example]"
log.Printf("BeforeSendTransaction event [%s]", event.EventID)
return event
},
// Enable tracing
EnableTracing: true,
// Specify either a TracesSampleRate...
Expand Down
19 changes: 14 additions & 5 deletions client.go
Expand Up @@ -140,8 +140,10 @@ type ClientOptions struct {
SendDefaultPII bool
// BeforeSend is called before error events are sent to Sentry.
// Use it to mutate the event or return nil to discard the event.
// See EventProcessor if you need to mutate transactions.
BeforeSend func(event *Event, hint *EventHint) *Event
// BeforeSendTransaction is called before transaction events are sent to Sentry.
// Use it to mutate the transaction or return nil to discard the transaction.
BeforeSendTransaction func(event *Event, hint *EventHint) *Event
// Before breadcrumb add callback.
BeforeBreadcrumb func(breadcrumb *Breadcrumb, hint *BreadcrumbHint) *Breadcrumb
// Integrations to be installed on the current Client, receives default
Expand Down Expand Up @@ -570,11 +572,18 @@ func (client *Client) processEvent(event *Event, hint *EventHint, scope EventMod
return nil
}

// As per spec, transactions do not go through BeforeSend.
if event.Type != transactionType && options.BeforeSend != nil {
if hint == nil {
hint = &EventHint{}
// Apply beforeSend* processors
if hint == nil {
hint = &EventHint{}
}
if event.Type == transactionType && options.BeforeSendTransaction != nil {
// Transaction events
if event = options.BeforeSendTransaction(event, hint); event == nil {
Logger.Println("Transaction dropped due to BeforeSendTransaction callback.")
return nil
}
} else if event.Type != transactionType && options.BeforeSend != nil {
// All other events
if event = options.BeforeSend(event, hint); event == nil {
Logger.Println("Event dropped due to BeforeSend callback.")
return nil
Expand Down
54 changes: 54 additions & 0 deletions client_test.go
Expand Up @@ -383,6 +383,60 @@ func TestBeforeSendGetAccessToEventHint(t *testing.T) {
assertEqual(t, transport.lastEvent.Message, "customComplexError: Foo 42")
}

func TestBeforeSendTransactionCanDropTransaction(t *testing.T) {
transport := &TransportMock{}
ctx := NewTestContext(ClientOptions{
EnableTracing: true,
TracesSampleRate: 1.0,
Transport: transport,
BeforeSend: func(event *Event, hint *EventHint) *Event {
t.Error("beforeSend should not be called")
return event
},
BeforeSendTransaction: func(event *Event, hint *EventHint) *Event {
assertEqual(t, event.Transaction, "Foo")
return nil
},
})

transaction := StartTransaction(ctx,
"Foo",
)
transaction.Finish()

if transport.lastEvent != nil {
t.Error("expected event to be dropped")
}
}

func TestBeforeSendTransactionIsCalled(t *testing.T) {
transport := &TransportMock{}
ctx := NewTestContext(ClientOptions{
EnableTracing: true,
TracesSampleRate: 1.0,
Transport: transport,
BeforeSend: func(event *Event, hint *EventHint) *Event {
t.Error("beforeSend should not be called")
return event
},
BeforeSendTransaction: func(event *Event, hint *EventHint) *Event {
assertEqual(t, event.Transaction, "Foo")
event.Transaction = "Bar"
return event
},
})

transaction := StartTransaction(ctx,
"Foo",
)
transaction.Finish()

lastEvent := transport.lastEvent
assertEqual(t, lastEvent.Transaction, "Bar")
// Make sure it's the same span
assertEqual(t, lastEvent.Contexts["trace"]["span_id"], transaction.SpanID)
}

func TestSampleRate(t *testing.T) {
tests := []struct {
SampleRate float64
Expand Down