configuration.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package bugsnag
  2. import (
  3. "log"
  4. "net/http"
  5. "path/filepath"
  6. "strings"
  7. )
  8. // Configuration sets up and customizes communication with the Bugsnag API.
  9. type Configuration struct {
  10. // Your Bugsnag API key, e.g. "c9d60ae4c7e70c4b6c4ebd3e8056d2b8". You can
  11. // find this by clicking Settings on https://bugsnag.com/.
  12. APIKey string
  13. // The Endpoint to notify about crashes. This defaults to
  14. // "https://notify.bugsnag.com/", if you're using Bugsnag Enterprise then
  15. // set it to your internal Bugsnag endpoint.
  16. Endpoint string
  17. // The current release stage. This defaults to "production" and is used to
  18. // filter errors in the Bugsnag dashboard.
  19. ReleaseStage string
  20. // A specialized type of the application, such as the worker queue or web
  21. // framework used, like "rails", "mailman", or "celery"
  22. AppType string
  23. // The currently running version of the app. This is used to filter errors
  24. // in the Bugsnag dasboard. If you set this then Bugsnag will only re-open
  25. // resolved errors if they happen in different app versions.
  26. AppVersion string
  27. // The hostname of the current server. This defaults to the return value of
  28. // os.Hostname() and is graphed in the Bugsnag dashboard.
  29. Hostname string
  30. // The Release stages to notify in. If you set this then bugsnag-go will
  31. // only send notifications to Bugsnag if the ReleaseStage is listed here.
  32. NotifyReleaseStages []string
  33. // packages that are part of your app. Bugsnag uses this to determine how
  34. // to group errors and how to display them on your dashboard. You should
  35. // include any packages that are part of your app, and exclude libraries
  36. // and helpers. You can list wildcards here, and they'll be expanded using
  37. // filepath.Glob. The default value is []string{"main*"}
  38. ProjectPackages []string
  39. // The SourceRoot is the directory where the application is built, and the
  40. // assumed prefix of lines on the stacktrace originating in the parent
  41. // application. When set, the prefix is trimmed from callstack file names
  42. // before ProjectPackages for better readability and to better group errors
  43. // on the Bugsnag dashboard. The default value is $GOPATH/src or $GOROOT/src
  44. // if $GOPATH is unset. At runtime, $GOROOT is the root used during the Go
  45. // build.
  46. SourceRoot string
  47. // Any meta-data that matches these filters will be marked as [REDACTED]
  48. // before sending a Notification to Bugsnag. It defaults to
  49. // []string{"password", "secret"} so that request parameters like password,
  50. // password_confirmation and auth_secret will not be sent to Bugsnag.
  51. ParamsFilters []string
  52. // The PanicHandler is used by Bugsnag to catch unhandled panics in your
  53. // application. The default panicHandler uses mitchellh's panicwrap library,
  54. // and you can disable this feature by passing an empty: func() {}
  55. PanicHandler func()
  56. // The logger that Bugsnag should log to. Uses the same defaults as go's
  57. // builtin logging package. bugsnag-go logs whenever it notifies Bugsnag
  58. // of an error, and when any error occurs inside the library itself.
  59. Logger interface {
  60. Printf(format string, v ...interface{}) // limited to the functions used
  61. }
  62. // The http Transport to use, defaults to the default http Transport. This
  63. // can be configured if you are in an environment like Google App Engine
  64. // that has stringent conditions on making http requests.
  65. Transport http.RoundTripper
  66. // Whether bugsnag should notify synchronously. This defaults to false which
  67. // causes bugsnag-go to spawn a new goroutine for each notification.
  68. Synchronous bool
  69. // TODO: remember to update the update() function when modifying this struct
  70. }
  71. func (config *Configuration) update(other *Configuration) *Configuration {
  72. if other.APIKey != "" {
  73. config.APIKey = other.APIKey
  74. }
  75. if other.Endpoint != "" {
  76. config.Endpoint = other.Endpoint
  77. }
  78. if other.Hostname != "" {
  79. config.Hostname = other.Hostname
  80. }
  81. if other.AppType != "" {
  82. config.AppType = other.AppType
  83. }
  84. if other.AppVersion != "" {
  85. config.AppVersion = other.AppVersion
  86. }
  87. if other.SourceRoot != "" {
  88. config.SourceRoot = other.SourceRoot
  89. }
  90. if other.ReleaseStage != "" {
  91. config.ReleaseStage = other.ReleaseStage
  92. }
  93. if other.ParamsFilters != nil {
  94. config.ParamsFilters = other.ParamsFilters
  95. }
  96. if other.ProjectPackages != nil {
  97. config.ProjectPackages = other.ProjectPackages
  98. }
  99. if other.Logger != nil {
  100. config.Logger = other.Logger
  101. }
  102. if other.NotifyReleaseStages != nil {
  103. config.NotifyReleaseStages = other.NotifyReleaseStages
  104. }
  105. if other.PanicHandler != nil {
  106. config.PanicHandler = other.PanicHandler
  107. }
  108. if other.Transport != nil {
  109. config.Transport = other.Transport
  110. }
  111. if other.Synchronous {
  112. config.Synchronous = true
  113. }
  114. return config
  115. }
  116. func (config *Configuration) merge(other *Configuration) *Configuration {
  117. return config.clone().update(other)
  118. }
  119. func (config *Configuration) clone() *Configuration {
  120. clone := *config
  121. return &clone
  122. }
  123. func (config *Configuration) isProjectPackage(pkg string) bool {
  124. for _, p := range config.ProjectPackages {
  125. if d, f := filepath.Split(p); f == "**" {
  126. if strings.HasPrefix(pkg, d) {
  127. return true
  128. }
  129. }
  130. if match, _ := filepath.Match(p, pkg); match {
  131. return true
  132. }
  133. }
  134. return false
  135. }
  136. func (config *Configuration) stripProjectPackages(file string) string {
  137. trimmedFile := file
  138. if strings.HasPrefix(trimmedFile, config.SourceRoot) {
  139. trimmedFile = strings.TrimPrefix(trimmedFile, config.SourceRoot)
  140. }
  141. for _, p := range config.ProjectPackages {
  142. if len(p) > 2 && p[len(p)-2] == '/' && p[len(p)-1] == '*' {
  143. p = p[:len(p)-1]
  144. } else if p[len(p)-1] == '*' && p[len(p)-2] == '*' {
  145. p = p[:len(p)-2]
  146. } else {
  147. p = p + "/"
  148. }
  149. if strings.HasPrefix(trimmedFile, p) {
  150. return strings.TrimPrefix(trimmedFile, p)
  151. }
  152. }
  153. return trimmedFile
  154. }
  155. func (config *Configuration) logf(fmt string, args ...interface{}) {
  156. if config != nil && config.Logger != nil {
  157. config.Logger.Printf(fmt, args...)
  158. } else {
  159. log.Printf(fmt, args...)
  160. }
  161. }
  162. func (config *Configuration) notifyInReleaseStage() bool {
  163. if config.NotifyReleaseStages == nil {
  164. return true
  165. }
  166. for _, r := range config.NotifyReleaseStages {
  167. if r == config.ReleaseStage {
  168. return true
  169. }
  170. }
  171. return false
  172. }