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: Export client.EventFromMessage, client.EventFromException, and event.SetException #607

Merged
merged 5 commits into from
May 2, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 14 additions & 38 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"math/rand"
"net/http"
"os"
"reflect"
"sort"
"strings"
"sync"
Expand Down Expand Up @@ -378,13 +377,13 @@ func (client Client) Options() ClientOptions {

// CaptureMessage captures an arbitrary message.
func (client *Client) CaptureMessage(message string, hint *EventHint, scope EventModifier) *EventID {
event := client.eventFromMessage(message, LevelInfo)
event := client.EventFromMessage(message, LevelInfo)
return client.CaptureEvent(event, hint, scope)
}

// CaptureException captures an error.
func (client *Client) CaptureException(exception error, hint *EventHint, scope EventModifier) *EventID {
event := client.eventFromException(exception, LevelError)
event := client.EventFromException(exception, LevelError)
return client.CaptureEvent(event, hint, scope)
}

Expand Down Expand Up @@ -439,11 +438,11 @@ func (client *Client) RecoverWithContext(
var event *Event
switch err := err.(type) {
case error:
event = client.eventFromException(err, LevelFatal)
event = client.EventFromException(err, LevelFatal)
case string:
event = client.eventFromMessage(err, LevelFatal)
event = client.EventFromMessage(err, LevelFatal)
default:
event = client.eventFromMessage(fmt.Sprintf("%#v", err), LevelFatal)
event = client.EventFromMessage(fmt.Sprintf("%#v", err), LevelFatal)
}
return client.CaptureEvent(event, hint, scope)
}
Expand All @@ -463,10 +462,11 @@ func (client *Client) Flush(timeout time.Duration) bool {
return client.Transport.Flush(timeout)
}

func (client *Client) eventFromMessage(message string, level Level) *Event {
// EventFromMessage creates an event from the given message string.
func (client *Client) EventFromMessage(message string, level Level) *Event {
if message == "" {
err := usageError{fmt.Errorf("%s called with empty message", callerFunctionName())}
return client.eventFromException(err, level)
return client.EventFromException(err, level)
}
event := NewEvent()
event.Level = level
Expand All @@ -483,41 +483,17 @@ func (client *Client) eventFromMessage(message string, level Level) *Event {
return event
}

func (client *Client) eventFromException(exception error, level Level) *Event {
err := exception
if err == nil {
err = usageError{fmt.Errorf("%s called with nil error", callerFunctionName())}
}

// EventFromException creates a new Sentry event from the given `error` instance.
func (client *Client) EventFromException(exception error, level Level) *Event {
event := NewEvent()
event.Level = level

for i := 0; i < client.options.MaxErrorDepth && err != nil; i++ {
event.Exception = append(event.Exception, Exception{
Value: err.Error(),
Type: reflect.TypeOf(err).String(),
Stacktrace: ExtractStacktrace(err),
})
switch previous := err.(type) {
case interface{ Unwrap() error }:
err = previous.Unwrap()
case interface{ Cause() error }:
err = previous.Cause()
default:
err = nil
}
}

// Add a trace of the current stack to the most recent error in a chain if
// it doesn't have a stack trace yet.
// We only add to the most recent error to avoid duplication and because the
// current stack is most likely unrelated to errors deeper in the chain.
if event.Exception[0].Stacktrace == nil {
event.Exception[0].Stacktrace = NewStacktrace()
err := exception
if err == nil {
err = usageError{fmt.Errorf("%s called with nil error", callerFunctionName())}
}

// event.Exception should be sorted such that the most recent error is last.
reverse(event.Exception)
event.SetException(err, client.options.MaxErrorDepth)

return event
}
Expand Down
39 changes: 39 additions & 0 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/http"
"reflect"
"strings"
"time"
)
Expand Down Expand Up @@ -313,6 +314,44 @@ type Event struct {
sdkMetaData SDKMetaData
}

// SetException appends the unwrapped errors to the event's exception list.
//
// maxErrorDepth is the maximum depth of the error chain we will look
// into while unwrapping the errors.
func (e *Event) SetException(exception error, maxErrorDepth int) {
err := exception
if err == nil {
return
}

for i := 0; i < maxErrorDepth && err != nil; i++ {
e.Exception = append(e.Exception, Exception{
Value: err.Error(),
Type: reflect.TypeOf(err).String(),
Stacktrace: ExtractStacktrace(err),
})
switch previous := err.(type) {
case interface{ Unwrap() error }:
err = previous.Unwrap()
case interface{ Cause() error }:
err = previous.Cause()
default:
err = nil
}
}

// Add a trace of the current stack to the most recent error in a chain if
// it doesn't have a stack trace yet.
// We only add to the most recent error to avoid duplication and because the
// current stack is most likely unrelated to errors deeper in the chain.
if e.Exception[0].Stacktrace == nil {
e.Exception[0].Stacktrace = NewStacktrace()
}

// event.Exception should be sorted such that the most recent error is last.
reverse(e.Exception)
}

// TODO: Event.Contexts map[string]interface{} => map[string]EventContext,
// to prevent accidentally storing T when we mean *T.
// For example, the TraceContext must be stored as *TraceContext to pick up the
Expand Down