client.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package honeybadger
  2. import (
  3. "net/http"
  4. "strings"
  5. )
  6. // The Payload interface is implemented by any type which can be handled by the
  7. // Backend interface.
  8. type Payload interface {
  9. toJSON() []byte
  10. }
  11. // The Backend interface is implemented by the server type by default, but a
  12. // custom implementation may be configured by the user.
  13. type Backend interface {
  14. Notify(feature Feature, payload Payload) error
  15. }
  16. type noticeHandler func(*Notice) error
  17. // Client is the manager for interacting with the Honeybadger service. It holds
  18. // the configuration and implements the public API.
  19. type Client struct {
  20. Config *Configuration
  21. context *contextSync
  22. worker worker
  23. beforeNotifyHandlers []noticeHandler
  24. }
  25. // Configure updates the client configuration with the supplied config.
  26. func (client *Client) Configure(config Configuration) {
  27. client.Config.update(&config)
  28. }
  29. // SetContext updates the client context with supplied context.
  30. func (client *Client) SetContext(context Context) {
  31. client.context.Update(context)
  32. }
  33. // Flush blocks until the worker has processed its queue.
  34. func (client *Client) Flush() {
  35. client.worker.Flush()
  36. }
  37. // BeforeNotify adds a callback function which is run before a notice is
  38. // reported to Honeybadger. If any function returns an error the notification
  39. // will be skipped, otherwise it will be sent.
  40. func (client *Client) BeforeNotify(handler func(notice *Notice) error) {
  41. client.beforeNotifyHandlers = append(client.beforeNotifyHandlers, handler)
  42. }
  43. // Notify reports the error err to the Honeybadger service.
  44. func (client *Client) Notify(err interface{}, extra ...interface{}) (string, error) {
  45. extra = append([]interface{}{client.context.internal}, extra...)
  46. notice := newNotice(client.Config, newError(err, 2), extra...)
  47. for _, handler := range client.beforeNotifyHandlers {
  48. if err := handler(notice); err != nil {
  49. return "", err
  50. }
  51. }
  52. workerErr := client.worker.Push(func() error {
  53. if err := client.Config.Backend.Notify(Notices, notice); err != nil {
  54. return err
  55. }
  56. return nil
  57. })
  58. if workerErr != nil {
  59. client.Config.Logger.Printf("worker error: %v\n", workerErr)
  60. return "", workerErr
  61. }
  62. return notice.Token, nil
  63. }
  64. // Monitor automatically reports panics which occur in the function it's called
  65. // from. Must be deferred.
  66. func (client *Client) Monitor() {
  67. if err := recover(); err != nil {
  68. client.Notify(newError(err, 2))
  69. client.Flush()
  70. panic(err)
  71. }
  72. }
  73. // Handler returns an http.Handler function which automatically reports panics
  74. // to Honeybadger and then re-panics.
  75. func (client *Client) Handler(h http.Handler) http.Handler {
  76. if h == nil {
  77. h = http.DefaultServeMux
  78. }
  79. fn := func(w http.ResponseWriter, r *http.Request) {
  80. defer func() {
  81. if err := recover(); err != nil {
  82. client.Notify(newError(err, 2), Params(r.Form), getCGIData(r), *r.URL)
  83. panic(err)
  84. }
  85. }()
  86. h.ServeHTTP(w, r)
  87. }
  88. return http.HandlerFunc(fn)
  89. }
  90. // New returns a new instance of Client.
  91. func New(c Configuration) *Client {
  92. config := newConfig(c)
  93. worker := newBufferedWorker(config)
  94. client := Client{
  95. Config: config,
  96. worker: worker,
  97. context: newContextSync(),
  98. }
  99. return &client
  100. }
  101. func getCGIData(request *http.Request) CGIData {
  102. cgiData := CGIData{}
  103. replacer := strings.NewReplacer("-", "_")
  104. for k, v := range request.Header {
  105. key := "HTTP_" + replacer.Replace(strings.ToUpper(k))
  106. cgiData[key] = v[0]
  107. }
  108. return cgiData
  109. }