Skip to content

Commit c7a259c

Browse files
committed
Remove Message type
Remove the redundant Message struct and simplify newEvent processing to a single pass. The event creation now only auto-assigns the error class when none has been set and always sets the event message from the error. Add feature scenarios and fixture handlers for custom ErrorClass/Message (including multiple values where last wins), update tests to cover these behaviors, and adjust the example app to remove the old sample Notify call. Also update example module dependencies and checksums.
1 parent 4486ecb commit c7a259c

9 files changed

Lines changed: 206 additions & 67 deletions

File tree

event.go

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ type ErrorClass struct {
2929
Name string
3030
}
3131

32-
// Message overrides the error message in Bugsnag.
33-
// This struct enables you to set a custom message for the error.
34-
type Message struct {
35-
String string
36-
}
37-
3832
// Sets the severity of the error on Bugsnag. These values can be
3933
// passed to Notify, Recover or AutoNotify as rawData.
4034
var (
@@ -134,32 +128,17 @@ func newEvent(rawData []interface{}, notifier *Notifier) (*Event, *Configuration
134128
var err *errors.Error
135129
var callbacks []func(*Event)
136130

137-
// FIRST PASS: Check if ErrorClass or Message are explicitly provided
138-
hasExplicitErrorClass := false
139-
hasExplicitMessage := false
140-
for _, datum := range event.RawData {
141-
switch datum.(type) {
142-
case ErrorClass:
143-
hasExplicitErrorClass = true
144-
case Message:
145-
hasExplicitMessage = true
146-
}
147-
}
148-
149-
// SECOND PASS: Process all data
150131
for _, datum := range event.RawData {
151132
switch datum := datum.(type) {
152133

153134
case error, errors.Error:
154135
err = errors.New(datum.(error), 1)
155136
event.Error = err
156-
// Only assign from error if NOT explicitly set
157-
if !hasExplicitErrorClass {
137+
// Only assign automatically if not explicitly set through ErrorClass already
138+
if event.ErrorClass == "" {
158139
event.ErrorClass = err.TypeName()
159140
}
160-
if !hasExplicitMessage {
161-
event.Message = err.Error()
162-
}
141+
event.Message = err.Error()
163142
event.Stacktrace = make([]StackFrame, len(err.StackFrames()))
164143

165144
case bool:
@@ -191,9 +170,6 @@ func newEvent(rawData []interface{}, notifier *Notifier) (*Event, *Configuration
191170
case ErrorClass:
192171
event.ErrorClass = datum.Name
193172

194-
case Message:
195-
event.Message = datum.String
196-
197173
case HandledState:
198174
event.handledState = datum
199175
event.Severity = datum.OriginalSeverity

examples/http/go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ require github.com/bugsnag/bugsnag-go/v2 v2.2.0
66

77
require (
88
github.com/bugsnag/panicwrap v1.3.4 // indirect
9-
github.com/google/uuid v1.6.0 // indirect
9+
github.com/google/uuid v1.3.0 // indirect
1010
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
1111
github.com/pkg/errors v0.9.1 // indirect
1212
)
13-
14-
replace github.com/bugsnag/bugsnag-go/v2 => ../../v2

examples/http/go.sum

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
2-
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
1+
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
2+
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
3+
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
4+
github.com/bugsnag/bugsnag-go/v2 v2.2.0 h1:y4JJ6xNJiK4jbmq/BLXe09MGUNRp/r1Zpye6RKcPJJ8=
5+
github.com/bugsnag/bugsnag-go/v2 v2.2.0/go.mod h1:Aoi1ax1kGbbkArShzXUQjxp6jM8gMh4qOtHLis/jY1E=
36
github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3pU=
47
github.com/bugsnag/panicwrap v1.3.4/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
5-
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
6-
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
8+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
9+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
710
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
811
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
12+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
13+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
14+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
915
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
1016
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

examples/http/main.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,4 @@ func handledError(w http.ResponseWriter, r *http.Request) {
4444
if err != nil {
4545
bugsnag.Notify(err, r.Context())
4646
}
47-
48-
// Example: Notify with custom error class and message
49-
bugsnag.Notify(
50-
fmt.Errorf(""),
51-
bugsnag.ErrorClass{Name: "CustomEvent"},
52-
bugsnag.Message{String: "Something custom happened"},
53-
r.Context(),
54-
)
5547
}

features/configuration.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Feature: Configure integration with environment variables
4545
And the "lineNumber" of stack frame 0 equals 11
4646
And the "file" of stack frame 1 equals "features/fixtures/app/main.go"
4747
And the "method" of stack frame 1 equals "main"
48-
And the "lineNumber" of stack frame 1 equals 65
48+
And the "lineNumber" of stack frame 1 equals 68
4949

5050
Scenario: Configuring source root
5151
Given I set environment variable "BUGSNAG_SOURCE_ROOT" to "/app/src/features/fixtures/app/"
@@ -58,7 +58,7 @@ Feature: Configure integration with environment variables
5858
And the "lineNumber" of stack frame 0 equals 11
5959
And the "file" of stack frame 1 equals "main.go"
6060
And the "method" of stack frame 1 equals "main"
61-
And the "lineNumber" of stack frame 1 equals 65
61+
And the "lineNumber" of stack frame 1 equals 68
6262

6363
Scenario: Delivering events filtering through notify release stages
6464
Given I set environment variable "BUGSNAG_NOTIFY_RELEASE_STAGES" to "prod,beta"

features/fixtures/app/handled_scenario.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,40 @@ func OnBeforeNotifyScenario(command Command) func() {
110110
}
111111
return scenarioFunc
112112
}
113+
114+
func CustomErrorClassAndMessageScenario(command Command) func() {
115+
scenarioFunc := func() {
116+
// Notify with custom ErrorClass and Message
117+
bugsnag.Notify(
118+
fmt.Errorf("original error"),
119+
bugsnag.ErrorClass{Name: "CustomErrorClass"},
120+
bugsnag.Message{String: "Custom error message"},
121+
)
122+
}
123+
return scenarioFunc
124+
}
125+
126+
func CustomMessageOnlyScenario(command Command) func() {
127+
scenarioFunc := func() {
128+
// Notify with custom Message only (ErrorClass from error type)
129+
bugsnag.Notify(
130+
fmt.Errorf("original error"),
131+
bugsnag.Message{String: "Custom message only"},
132+
)
133+
}
134+
return scenarioFunc
135+
}
136+
137+
func MultipleErrorClassAndMessageScenario(command Command) func() {
138+
scenarioFunc := func() {
139+
// Notify with multiple ErrorClass and Message - last one wins
140+
bugsnag.Notify(
141+
fmt.Errorf("original error"),
142+
bugsnag.ErrorClass{Name: "FirstErrorClass"},
143+
bugsnag.Message{String: "First message"},
144+
bugsnag.ErrorClass{Name: "LastErrorClass"},
145+
bugsnag.Message{String: "Last message"},
146+
)
147+
}
148+
return scenarioFunc
149+
}

features/fixtures/app/main.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,28 @@ import (
99
)
1010

1111
var scenariosMap = map[string]func(Command) func(){
12-
"UnhandledCrashScenario": UnhandledCrashScenario,
13-
"HandledErrorScenario": HandledErrorScenario,
14-
"MultipleUnhandledErrorsScenario": MultipleUnhandledErrorsScenario,
15-
"MultipleHandledErrorsScenario": MultipleHandledErrorsScenario,
16-
"NestedHandledErrorScenario": NestedHandledErrorScenario,
17-
"MetadataScenario": MetadataScenario,
18-
"FilteredMetadataScenario": FilteredMetadataScenario,
19-
"HandledCallbackErrorScenario": HandledCallbackErrorScenario,
20-
"SendSessionScenario": SendSessionScenario,
21-
"HandledToUnhandledScenario": HandledToUnhandledScenario,
22-
"SetUserScenario": SetUserScenario,
23-
"RecoverAfterPanicScenario": RecoverAfterPanicScenario,
24-
"AutonotifyPanicScenario": AutonotifyPanicScenario,
25-
"SessionAndErrorScenario": SessionAndErrorScenario,
26-
"OnBeforeNotifyScenario": OnBeforeNotifyScenario,
27-
"AutoconfigPanicScenario": AutoconfigPanicScenario,
28-
"AutoconfigHandledScenario": AutoconfigHandledScenario,
29-
"AutoconfigMetadataScenario": AutoconfigMetadataScenario,
30-
"HttpServerScenario": HttpServerScenario,
12+
"UnhandledCrashScenario": UnhandledCrashScenario,
13+
"HandledErrorScenario": HandledErrorScenario,
14+
"MultipleUnhandledErrorsScenario": MultipleUnhandledErrorsScenario,
15+
"MultipleHandledErrorsScenario": MultipleHandledErrorsScenario,
16+
"NestedHandledErrorScenario": NestedHandledErrorScenario,
17+
"MetadataScenario": MetadataScenario,
18+
"FilteredMetadataScenario": FilteredMetadataScenario,
19+
"HandledCallbackErrorScenario": HandledCallbackErrorScenario,
20+
"SendSessionScenario": SendSessionScenario,
21+
"HandledToUnhandledScenario": HandledToUnhandledScenario,
22+
"SetUserScenario": SetUserScenario,
23+
"RecoverAfterPanicScenario": RecoverAfterPanicScenario,
24+
"AutonotifyPanicScenario": AutonotifyPanicScenario,
25+
"SessionAndErrorScenario": SessionAndErrorScenario,
26+
"OnBeforeNotifyScenario": OnBeforeNotifyScenario,
27+
"AutoconfigPanicScenario": AutoconfigPanicScenario,
28+
"AutoconfigHandledScenario": AutoconfigHandledScenario,
29+
"AutoconfigMetadataScenario": AutoconfigMetadataScenario,
30+
"HttpServerScenario": HttpServerScenario,
31+
"CustomErrorClassAndMessageScenario": CustomErrorClassAndMessageScenario,
32+
"CustomMessageOnlyScenario": CustomMessageOnlyScenario,
33+
"MultipleErrorClassAndMessageScenario": MultipleErrorClassAndMessageScenario,
3134
}
3235

3336
func main() {

features/handled.feature

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,37 @@ Scenario: Unwrapping the causes of a handled error
6464
And the event "exceptions.1.stacktrace.0.lineNumber" equals 39
6565
And the event "exceptions.2.message" equals "invalid token"
6666
And the event "exceptions.2.stacktrace.0.file" equals "utils.go"
67-
And the event "exceptions.2.stacktrace.0.lineNumber" equals 47
67+
And the event "exceptions.2.stacktrace.0.lineNumber" equals 47
68+
69+
Scenario: A handled error with custom ErrorClass and Message
70+
When I start the service "app"
71+
And I run "CustomErrorClassAndMessageScenario"
72+
And I wait to receive an error
73+
And the event "unhandled" is false
74+
And the event "severity" equals "warning"
75+
And the event "severityReason.type" equals "handledError"
76+
And the exception "errorClass" equals "CustomErrorClass"
77+
And the exception "message" equals "Custom error message"
78+
And the "file" of stack frame 0 equals "handled_scenario.go"
79+
80+
Scenario: A handled error with custom Message only
81+
When I start the service "app"
82+
And I run "CustomMessageOnlyScenario"
83+
And I wait to receive an error
84+
And the event "unhandled" is false
85+
And the event "severity" equals "warning"
86+
And the event "severityReason.type" equals "handledError"
87+
And the exception "errorClass" equals "*errors.errorString"
88+
And the exception "message" equals "Custom message only"
89+
And the "file" of stack frame 0 equals "handled_scenario.go"
90+
91+
Scenario: A handled error with multiple ErrorClass and Message uses the last values
92+
When I start the service "app"
93+
And I run "MultipleErrorClassAndMessageScenario"
94+
And I wait to receive an error
95+
And the event "unhandled" is false
96+
And the event "severity" equals "warning"
97+
And the event "severityReason.type" equals "handledError"
98+
And the exception "errorClass" equals "LastErrorClass"
99+
And the exception "message" equals "Last message"
100+
And the "file" of stack frame 0 equals "handled_scenario.go"

v2/bugsnag_test.go

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ func TestNotify(t *testing.T) {
145145
func TestNotifyWithCustomMessage(t *testing.T) {
146146
ts, reports := setup()
147147
defer ts.Close()
148-
sessionTracker = nil
149-
startSessionTracking()
150148

151149
ctx, cancel := setupState()
152150
defer cancel()
@@ -165,7 +163,6 @@ func TestNotifyWithCustomMessage(t *testing.T) {
165163
App: &appJSON{ReleaseStage: "test", Type: "foo", Version: "1.2.3"},
166164
Context: "",
167165
Device: &deviceJSON{Hostname: "web1"},
168-
Session: &sessionJSON{Events: sessions.EventCounts{Handled: 0, Unhandled: 1}},
169166
Severity: "warning",
170167
SeverityReason: &severityReasonJSON{Type: SeverityReasonHandledError},
171168
Unhandled: false,
@@ -175,6 +172,103 @@ func TestNotifyWithCustomMessage(t *testing.T) {
175172
})
176173
}
177174

175+
func TestNotifyWithMultipleErrorClassAndMessage(t *testing.T) {
176+
ts, reports := setup()
177+
defer ts.Close()
178+
179+
ctx, cancel := setupState()
180+
defer cancel()
181+
config := generateSampleConfig(ts.URL, ctx)
182+
183+
// Test with multiple ErrorClass and Message - last one should win
184+
Notify(
185+
fmt.Errorf("original error"),
186+
config,
187+
ErrorClass{Name: "FirstErrorClass"},
188+
Message{String: "First message"},
189+
ErrorClass{Name: "SecondErrorClass"},
190+
Message{String: "Second message"},
191+
)
192+
193+
json, err := simplejson.NewJson(<-reports)
194+
195+
if err != nil {
196+
t.Fatal(err)
197+
}
198+
199+
assertPayload(t, json, eventJSON{
200+
App: &appJSON{ReleaseStage: "test", Type: "foo", Version: "1.2.3"},
201+
Context: "",
202+
Device: &deviceJSON{Hostname: "web1"},
203+
Severity: "warning",
204+
SeverityReason: &severityReasonJSON{Type: SeverityReasonHandledError},
205+
Unhandled: false,
206+
Request: &RequestJSON{},
207+
User: &User{},
208+
Exceptions: []exceptionJSON{{ErrorClass: "SecondErrorClass", Message: "Second message"}},
209+
})
210+
}
211+
212+
func TestNotifyWithOnlyErrorClass(t *testing.T) {
213+
ts, reports := setup()
214+
defer ts.Close()
215+
216+
ctx, cancel := setupState()
217+
defer cancel()
218+
config := generateSampleConfig(ts.URL, ctx)
219+
220+
// Test with only custom ErrorClass (message from error)
221+
Notify(fmt.Errorf("error message from error"), config, ErrorClass{Name: "CustomErrorClass"})
222+
223+
json, err := simplejson.NewJson(<-reports)
224+
225+
if err != nil {
226+
t.Fatal(err)
227+
}
228+
229+
assertPayload(t, json, eventJSON{
230+
App: &appJSON{ReleaseStage: "test", Type: "foo", Version: "1.2.3"},
231+
Context: "",
232+
Device: &deviceJSON{Hostname: "web1"},
233+
Severity: "warning",
234+
SeverityReason: &severityReasonJSON{Type: SeverityReasonHandledError},
235+
Unhandled: false,
236+
Request: &RequestJSON{},
237+
User: &User{},
238+
Exceptions: []exceptionJSON{{ErrorClass: "CustomErrorClass", Message: "error message from error"}},
239+
})
240+
}
241+
242+
func TestNotifyWithOnlyMessage(t *testing.T) {
243+
ts, reports := setup()
244+
defer ts.Close()
245+
246+
ctx, cancel := setupState()
247+
defer cancel()
248+
config := generateSampleConfig(ts.URL, ctx)
249+
250+
// Test with only custom Message (error class from error type)
251+
Notify(fmt.Errorf("original error"), config, Message{String: "custom message only"})
252+
253+
json, err := simplejson.NewJson(<-reports)
254+
255+
if err != nil {
256+
t.Fatal(err)
257+
}
258+
259+
assertPayload(t, json, eventJSON{
260+
App: &appJSON{ReleaseStage: "test", Type: "foo", Version: "1.2.3"},
261+
Context: "",
262+
Device: &deviceJSON{Hostname: "web1"},
263+
Severity: "warning",
264+
SeverityReason: &severityReasonJSON{Type: SeverityReasonHandledError},
265+
Unhandled: false,
266+
Request: &RequestJSON{},
267+
User: &User{},
268+
Exceptions: []exceptionJSON{{ErrorClass: "*errors.errorString", Message: "custom message only"}},
269+
})
270+
}
271+
178272
type testPublisher struct {
179273
sync bool
180274
}

0 commit comments

Comments
 (0)