forked from bugsnag/bugsnag-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
notifier.go
151 lines (132 loc) · 5.1 KB
/
notifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package bugsnag
import (
"github.com/bugsnag/bugsnag-go/errors"
)
var publisher reportPublisher = new(defaultReportPublisher)
// Notifier sends errors to Bugsnag.
type Notifier struct {
Config *Configuration
RawData []interface{}
}
// New creates a new notifier.
// You can pass an instance of bugsnag.Configuration in rawData to change the configuration.
// Other values of rawData will be passed to Notify.
func New(rawData ...interface{}) *Notifier {
config := Config.clone()
for i, datum := range rawData {
if c, ok := datum.(Configuration); ok {
config.update(&c)
rawData[i] = nil
}
}
return &Notifier{
Config: config,
RawData: rawData,
}
}
// FlushSessionsOnRepanic takes a boolean that indicates whether sessions
// should be flushed when AutoNotify repanics. In the case of a fatal panic the
// sessions might not get sent to Bugsnag before the application shuts down.
// Many frameworks will have their own error handler, and for these frameworks
// there is no need to flush sessions as the application will survive the panic
// and the sessions can be sent off later. The default value is true, so this
// needs only be called if you wish to inform Bugsnag that there is an error
// handler that will take care of panics that AutoNotify will re-raise.
func (notifier *Notifier) FlushSessionsOnRepanic(shouldFlush bool) {
notifier.Config.flushSessionsOnRepanic = shouldFlush
}
// Notify sends an error to Bugsnag. Any rawData you pass here will be sent to
// Bugsnag after being converted to JSON. e.g. bugsnag.SeverityError, bugsnag.Context,
// or bugsnag.MetaData. Any bools in rawData overrides the
// notifier.Config.Synchronous flag.
func (notifier *Notifier) Notify(err error, rawData ...interface{}) (e error) {
if e := checkForEmptyError(err); e != nil {
return e
}
// Stripping one stackframe to not include this function in the stacktrace
// for a manual notification.
skipFrames := 1
return notifier.NotifySync(errors.New(err, skipFrames), notifier.Config.Synchronous, rawData...)
}
// NotifySync sends an error to Bugsnag. A boolean parameter specifies whether
// to send the report in the current context (by default false, i.e.
// asynchronous). Any other rawData you pass here will be sent to Bugsnag after
// being converted to JSON. E.g. bugsnag.SeverityError, bugsnag.Context, or
// bugsnag.MetaData.
func (notifier *Notifier) NotifySync(err error, sync bool, rawData ...interface{}) error {
if e := checkForEmptyError(err); e != nil {
return e
}
// Stripping one stackframe to not include this function in the stacktrace
// for a manual notification.
skipFrames := 1
event, config := newEvent(append(rawData, errors.New(err, skipFrames), sync), notifier)
// Never block, start throwing away errors if we have too many.
e := middleware.Run(event, config, func() error {
return publisher.publishReport(&payload{event, config})
})
if e != nil {
config.logf("bugsnag.Notify: %v", e)
}
return e
}
// AutoNotify notifies Bugsnag of any panics, then repanics.
// It sends along any rawData that gets passed in.
// Usage:
// go func() {
// defer AutoNotify()
// // (possibly crashy code)
// }()
func (notifier *Notifier) AutoNotify(rawData ...interface{}) {
if err := recover(); err != nil {
severity := notifier.getDefaultSeverity(rawData, SeverityError)
state := HandledState{SeverityReasonHandledPanic, severity, true, ""}
rawData = notifier.appendStateIfNeeded(rawData, state)
// We strip the following stackframes as they don't add much
// information but would mess with the grouping algorithm
// { "file": "github.com/bugsnag/bugsnag-go/notifier.go", "lineNumber": 116, "method": "(*Notifier).AutoNotify" },
// { "file": "runtime/asm_amd64.s", "lineNumber": 573, "method": "call32" },
skipFrames := 2
notifier.NotifySync(errors.New(err, skipFrames), true, rawData...)
panic(err)
}
}
// Recover logs any panics, then recovers.
// It sends along any rawData that gets passed in.
// Usage: defer Recover()
func (notifier *Notifier) Recover(rawData ...interface{}) {
if err := recover(); err != nil {
severity := notifier.getDefaultSeverity(rawData, SeverityWarning)
state := HandledState{SeverityReasonHandledPanic, severity, false, ""}
rawData = notifier.appendStateIfNeeded(rawData, state)
notifier.Notify(errors.New(err, 2), rawData...)
}
}
func (notifier *Notifier) dontPanic() {
if err := recover(); err != nil {
notifier.Config.logf("bugsnag/notifier.Notify: panic! %s", err)
}
}
// Get defined severity from raw data or a fallback value
func (notifier *Notifier) getDefaultSeverity(rawData []interface{}, s severity) severity {
allData := append(notifier.RawData, rawData...)
for _, datum := range allData {
if _, ok := datum.(severity); ok {
return datum.(severity)
}
}
for _, datum := range allData {
if _, ok := datum.(HandledState); ok {
return datum.(HandledState).OriginalSeverity
}
}
return s
}
func (notifier *Notifier) appendStateIfNeeded(rawData []interface{}, h HandledState) []interface{} {
for _, datum := range append(notifier.RawData, rawData...) {
if _, ok := datum.(HandledState); ok {
return rawData
}
}
return append(rawData, h)
}