notifier.go 5.1 KB

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