From c227fda25fdb5b06328e7842613775176fb7c54e Mon Sep 17 00:00:00 2001 From: Valentin Krasontovitsch Date: Mon, 20 Nov 2017 11:02:27 +0100 Subject: [PATCH] Add methods that return error when capturing The client's capture method returns an error channel that according to the docs is intended to be used for checking if a packet was sent successfully whenever that is important. The `...AndWait` methods use this channel, but only to wait. They do not capture the possible error coming from that channel. The changes in this commit suggest to use the error and return it, so that a user may check whether a packet was sent successfully using new top level methods, instead of having to write their own. The newly introduced methods follow the naming scheme `Capture$SOMETHINGAndConfirm`, where the returned error is considered confirmation. Work on #84 --- client.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index d996410..c7c88b8 100644 --- a/client.go +++ b/client.go @@ -664,6 +664,30 @@ func CaptureMessageAndWait(message string, tags map[string]string, interfaces .. return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...) } +// CaptureMessageAndConfirm is identical to CaptureMessage except it blocks and +// waits for the message to be sent and returns an error. +func (client *Client) CaptureMessageAndConfirm(message string, tags map[string]string, interfaces ...Interface) (error, string) { + if client == nil { + return nil, "" + } + + if client.shouldExcludeErr(message) { + return nil, "" + } + + packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...) + eventID, ch := client.Capture(packet, tags) + internalError := <-ch + + return internalError, eventID +} + +// CaptureMessageAndConfirm is identical to CaptureMessage except it blocks and +// waits for the message to be sent and returns an error. +func CaptureMessageAndConfirm(message string, tags map[string]string, interfaces ...Interface) (error, string) { + return DefaultClient.CaptureMessageAndConfirm(message, tags, interfaces...) +} + // CaptureErrors formats and delivers an error to the Sentry server. // Adds a stacktrace to the packet, excluding the call to this method. func (client *Client) CaptureError(err error, tags map[string]string, interfaces ...Interface) string { @@ -719,6 +743,30 @@ func CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interf return DefaultClient.CaptureErrorAndWait(err, tags, interfaces...) } +// CaptureErrorAndConfirm is identical to CaptureError, except it blocks and +// assures that the event was sent and returns an error +func (client *Client) CaptureErrorAndConfirm(err error, tags map[string]string, interfaces ...Interface) (error, string) { + if client == nil { + return nil, "" + } + + if client.shouldExcludeErr(err.Error()) { + return nil, "" + } + + packet := NewPacket(err.Error(), append(append(interfaces, client.context.interfaces()...), NewException(err, NewStacktrace(1, 3, client.includePaths)))...) + eventID, ch := client.Capture(packet, tags) + internalError := <-ch + + return internalError, eventID +} + +// CaptureErrorAndConfirm is identical to CaptureError, except it blocks and +// assures that the event was sent and returns an error +func CaptureErrorAndConfirm(err error, tags map[string]string, interfaces ...Interface) (error, string) { + return DefaultClient.CaptureErrorAndConfirm(err, tags, interfaces...) +} + // CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs. // If an error is captured, both the error and the reported Sentry error ID are returned. func (client *Client) CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) { @@ -758,7 +806,7 @@ func CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (in return DefaultClient.CapturePanic(f, tags, interfaces...) } -// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent +// CapturePanicAndWait is identical to CapturePanic, except it blocks and assures that the event was sent func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) { // Note: This doesn't need to check for client, because we still want to go through the defer/recover path // Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the @@ -794,11 +842,52 @@ func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, inte return } -// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent +// CapturePanicAndWait is identical to CapturePanic, except it blocks and assures that the event was sent func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) { return DefaultClient.CapturePanicAndWait(f, tags, interfaces...) } +// CapturePanicAndConfirm is identical to CapturePanic, except it blocks and +// assures that the event was sent and returns an error +func (client *Client) CapturePanicAndConfirm(f func(), tags map[string]string, interfaces ...Interface) (internalError error, err interface{}, errorID string) { + // Note: This doesn't need to check for client, because we still want to go through the defer/recover path + // Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the + // *Packet just to be thrown away, this should not be the normal case. Could be refactored to + // be completely noop though if we cared. + defer func() { + var packet *Packet + err = recover() + switch rval := err.(type) { + case nil: + return + case error: + if client.shouldExcludeErr(rval.Error()) { + return + } + packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...) + default: + rvalStr := fmt.Sprint(rval) + if client.shouldExcludeErr(rvalStr) { + return + } + packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...) + } + + var ch chan error + errorID, ch = client.Capture(packet, tags) + internalError = <-ch + }() + + f() + return +} + +// CapturePanicAndConfirm is identical to CapturePanic, except it blocks and +// assures that the event was sent and returns an error +func CapturePanicAndConfirm(f func(), tags map[string]string, interfaces ...Interface) (error, interface{}, string) { + return DefaultClient.CapturePanicAndConfirm(f, tags, interfaces...) +} + func (client *Client) Close() { close(client.queue) }