payload.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package bugsnag
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "net/http"
  7. "sync"
  8. "time"
  9. "github.com/bugsnag/bugsnag-go/headers"
  10. "github.com/bugsnag/bugsnag-go/sessions"
  11. )
  12. const notifyPayloadVersion = "4"
  13. var sessionMutex sync.Mutex
  14. type payload struct {
  15. *Event
  16. *Configuration
  17. }
  18. type hash map[string]interface{}
  19. func (p *payload) deliver() error {
  20. if len(p.APIKey) != 32 {
  21. return fmt.Errorf("bugsnag/payload.deliver: invalid api key: '%s'", p.APIKey)
  22. }
  23. buf, err := p.MarshalJSON()
  24. if err != nil {
  25. return fmt.Errorf("bugsnag/payload.deliver: %v", err)
  26. }
  27. client := http.Client{
  28. Transport: p.Transport,
  29. }
  30. req, err := http.NewRequest("POST", p.Endpoints.Notify, bytes.NewBuffer(buf))
  31. if err != nil {
  32. return fmt.Errorf("bugsnag/payload.deliver unable to create request: %v", err)
  33. }
  34. for k, v := range headers.PrefixedHeaders(p.APIKey, notifyPayloadVersion) {
  35. req.Header.Add(k, v)
  36. }
  37. resp, err := client.Do(req)
  38. if err != nil {
  39. return fmt.Errorf("bugsnag/payload.deliver: %v", err)
  40. }
  41. defer resp.Body.Close()
  42. if resp.StatusCode != 200 {
  43. return fmt.Errorf("bugsnag/payload.deliver: Got HTTP %s", resp.Status)
  44. }
  45. return nil
  46. }
  47. func (p *payload) MarshalJSON() ([]byte, error) {
  48. return json.Marshal(reportJSON{
  49. APIKey: p.APIKey,
  50. Events: []eventJSON{
  51. eventJSON{
  52. App: &appJSON{
  53. ReleaseStage: p.ReleaseStage,
  54. Type: p.AppType,
  55. Version: p.AppVersion,
  56. },
  57. Context: p.Context,
  58. Device: &deviceJSON{Hostname: p.Hostname},
  59. Request: p.Request,
  60. Exceptions: []exceptionJSON{
  61. exceptionJSON{
  62. ErrorClass: p.ErrorClass,
  63. Message: p.Message,
  64. Stacktrace: p.Stacktrace,
  65. },
  66. },
  67. GroupingHash: p.GroupingHash,
  68. Metadata: p.MetaData.sanitize(p.ParamsFilters),
  69. PayloadVersion: notifyPayloadVersion,
  70. Session: p.makeSession(),
  71. Severity: p.Severity.String,
  72. SeverityReason: p.severityReasonPayload(),
  73. Unhandled: p.handledState.Unhandled,
  74. User: p.User,
  75. },
  76. },
  77. Notifier: notifierJSON{
  78. Name: "Bugsnag Go",
  79. URL: "https://github.com/bugsnag/bugsnag-go",
  80. Version: VERSION,
  81. },
  82. })
  83. }
  84. func (p *payload) makeSession() *sessionJSON {
  85. // If a context has not been applied to the payload then assume that no
  86. // session has started either
  87. if p.Ctx == nil {
  88. return nil
  89. }
  90. sessionMutex.Lock()
  91. defer sessionMutex.Unlock()
  92. session := sessions.IncrementEventCountAndGetSession(p.Ctx, p.handledState.Unhandled)
  93. if session != nil {
  94. s := *session
  95. return &sessionJSON{
  96. ID: s.ID,
  97. StartedAt: s.StartedAt.UTC().Format(time.RFC3339),
  98. Events: sessions.EventCounts{
  99. Handled: s.EventCounts.Handled,
  100. Unhandled: s.EventCounts.Unhandled,
  101. },
  102. }
  103. }
  104. return nil
  105. }
  106. func (p *payload) severityReasonPayload() *severityReasonJSON {
  107. if reason := p.handledState.SeverityReason; reason != "" {
  108. return &severityReasonJSON{Type: reason}
  109. }
  110. return nil
  111. }