Skip to content

Commit

Permalink
feat: Add BeforeSendTransaction hook (#517)
Browse files Browse the repository at this point in the history
BeforeSendTransaction works similarly to BeforeSend, but it is applied only to transaction events.
  • Loading branch information
tonyo committed Jan 11, 2023
1 parent 3c8662b commit 932dcf0
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 6 deletions.
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

0 comments on commit 932dcf0

Please sign in to comment.