| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- package bugsnag
- import (
- "log"
- "net/http"
- "path/filepath"
- "strings"
- )
- // Endpoints hold the HTTP endpoints of the notifier.
- type Endpoints struct {
- Sessions string
- Notify string
- }
- // Configuration sets up and customizes communication with the Bugsnag API.
- type Configuration struct {
- // Your Bugsnag API key, e.g. "c9d60ae4c7e70c4b6c4ebd3e8056d2b8". You can
- // find this by clicking Settings on https://bugsnag.com/.
- APIKey string
- // Deprecated: Use Endpoints (with an 's') instead.
- // The Endpoint to notify about crashes. This defaults to
- // "https://notify.bugsnag.com/", if you're using Bugsnag Enterprise then
- // set it to your internal Bugsnag endpoint.
- Endpoint string
- // Endpoints define the HTTP endpoints that the notifier should notify
- // about crashes and sessions. These default to notify.bugsnag.com for
- // error reports and sessions.bugsnag.com for sessions.
- // If you are using bugsnag on-premise you will have to set these to your
- // Event Server and Session Server endpoints. If the notify endpoint is set
- // but the sessions endpoint is not, session tracking will be disabled
- // automatically to avoid leaking session information outside of your
- // server configuration, and a warning will be logged.
- Endpoints Endpoints
- // The current release stage. This defaults to "production" and is used to
- // filter errors in the Bugsnag dashboard.
- ReleaseStage string
- // A specialized type of the application, such as the worker queue or web
- // framework used, like "rails", "mailman", or "celery"
- AppType string
- // The currently running version of the app. This is used to filter errors
- // in the Bugsnag dasboard. If you set this then Bugsnag will only re-open
- // resolved errors if they happen in different app versions.
- AppVersion string
- // AutoCaptureSessions can be set to false to disable automatic session
- // tracking. If you want control over what is deemed a session, you can
- // switch off automatic session tracking with this configuration, and call
- // bugsnag.StartSession() when appropriate for your application. See the
- // official docs for instructions and examples of associating handled
- // errors with sessions and ensuring error rate accuracy on the Bugsnag
- // dashboard. This will default to true, but is stored as an interface to enable
- // us to detect when this option has not been set.
- AutoCaptureSessions interface{}
- // The hostname of the current server. This defaults to the return value of
- // os.Hostname() and is graphed in the Bugsnag dashboard.
- Hostname string
- // The Release stages to notify in. If you set this then bugsnag-go will
- // only send notifications to Bugsnag if the ReleaseStage is listed here.
- NotifyReleaseStages []string
- // packages that are part of your app. Bugsnag uses this to determine how
- // to group errors and how to display them on your dashboard. You should
- // include any packages that are part of your app, and exclude libraries
- // and helpers. You can list wildcards here, and they'll be expanded using
- // filepath.Glob. The default value is []string{"main*"}
- ProjectPackages []string
- // The SourceRoot is the directory where the application is built, and the
- // assumed prefix of lines on the stacktrace originating in the parent
- // application. When set, the prefix is trimmed from callstack file names
- // before ProjectPackages for better readability and to better group errors
- // on the Bugsnag dashboard. The default value is $GOPATH/src or $GOROOT/src
- // if $GOPATH is unset. At runtime, $GOROOT is the root used during the Go
- // build.
- SourceRoot string
- // Any meta-data that matches these filters will be marked as [FILTERED]
- // before sending a Notification to Bugsnag. It defaults to
- // []string{"password", "secret"} so that request parameters like password,
- // password_confirmation and auth_secret will not be sent to Bugsnag.
- ParamsFilters []string
- // The PanicHandler is used by Bugsnag to catch unhandled panics in your
- // application. The default panicHandler uses mitchellh's panicwrap library,
- // and you can disable this feature by passing an empty: func() {}
- PanicHandler func()
- // The logger that Bugsnag should log to. Uses the same defaults as go's
- // builtin logging package. bugsnag-go logs whenever it notifies Bugsnag
- // of an error, and when any error occurs inside the library itself.
- Logger interface {
- Printf(format string, v ...interface{}) // limited to the functions used
- }
- // The http Transport to use, defaults to the default http Transport. This
- // can be configured if you are in an environment like Google App Engine
- // that has stringent conditions on making http requests.
- Transport http.RoundTripper
- // Whether bugsnag should notify synchronously. This defaults to false which
- // causes bugsnag-go to spawn a new goroutine for each notification.
- Synchronous bool
- // Whether the notifier should send all sessions recorded so far to Bugsnag
- // when repanicking to ensure that no session information is lost in a
- // fatal crash.
- flushSessionsOnRepanic bool
- // TODO: remember to update the update() function when modifying this struct
- }
- func (config *Configuration) update(other *Configuration) *Configuration {
- if other.APIKey != "" {
- config.APIKey = other.APIKey
- }
- if other.Hostname != "" {
- config.Hostname = other.Hostname
- }
- if other.AppType != "" {
- config.AppType = other.AppType
- }
- if other.AppVersion != "" {
- config.AppVersion = other.AppVersion
- }
- if other.SourceRoot != "" {
- config.SourceRoot = other.SourceRoot
- }
- if other.ReleaseStage != "" {
- config.ReleaseStage = other.ReleaseStage
- }
- if other.ParamsFilters != nil {
- config.ParamsFilters = other.ParamsFilters
- }
- if other.ProjectPackages != nil {
- config.ProjectPackages = other.ProjectPackages
- }
- if other.Logger != nil {
- config.Logger = other.Logger
- }
- if other.NotifyReleaseStages != nil {
- config.NotifyReleaseStages = other.NotifyReleaseStages
- }
- if other.PanicHandler != nil {
- config.PanicHandler = other.PanicHandler
- }
- if other.Transport != nil {
- config.Transport = other.Transport
- }
- if other.Synchronous {
- config.Synchronous = true
- }
- if other.AutoCaptureSessions != nil {
- config.AutoCaptureSessions = other.AutoCaptureSessions
- }
- config.updateEndpoints(other.Endpoint, &other.Endpoints)
- return config
- }
- // IsAutoCaptureSessions identifies whether or not the notifier should
- // automatically capture sessions as requests come in. It's a convenience
- // wrapper that allows automatic session capturing to be enabled by default.
- func (config *Configuration) IsAutoCaptureSessions() bool {
- if config.AutoCaptureSessions == nil {
- return true // enabled by default
- }
- if val, ok := config.AutoCaptureSessions.(bool); ok {
- return val
- }
- // It has been configured to *something* (although not a valid value)
- // assume the user wanted to disable this option.
- return false
- }
- func (config *Configuration) updateEndpoints(endpoint string, endpoints *Endpoints) {
- if endpoint != "" {
- config.Logger.Printf("WARNING: the 'Endpoint' Bugsnag configuration parameter is deprecated in favor of 'Endpoints'")
- config.Endpoints.Notify = endpoint
- config.Endpoints.Sessions = ""
- }
- if endpoints.Notify != "" {
- config.Endpoints.Notify = endpoints.Notify
- if endpoints.Sessions == "" {
- config.Logger.Printf("WARNING: Bugsnag notify endpoint configured without also configuring the sessions endpoint. No sessions will be recorded")
- config.Endpoints.Sessions = ""
- }
- }
- if endpoints.Sessions != "" {
- if endpoints.Notify == "" {
- panic("FATAL: Bugsnag sessions endpoint configured without also changing the notify endpoint. Bugsnag cannot identify where to report errors")
- }
- config.Endpoints.Sessions = endpoints.Sessions
- }
- }
- func (config *Configuration) merge(other *Configuration) *Configuration {
- return config.clone().update(other)
- }
- func (config *Configuration) clone() *Configuration {
- clone := *config
- return &clone
- }
- func (config *Configuration) isProjectPackage(pkg string) bool {
- for _, p := range config.ProjectPackages {
- if d, f := filepath.Split(p); f == "**" {
- if strings.HasPrefix(pkg, d) {
- return true
- }
- }
- if match, _ := filepath.Match(p, pkg); match {
- return true
- }
- }
- return false
- }
- func (config *Configuration) stripProjectPackages(file string) string {
- trimmedFile := file
- if strings.HasPrefix(trimmedFile, config.SourceRoot) {
- trimmedFile = strings.TrimPrefix(trimmedFile, config.SourceRoot)
- }
- for _, p := range config.ProjectPackages {
- if len(p) > 2 && p[len(p)-2] == '/' && p[len(p)-1] == '*' {
- p = p[:len(p)-1]
- } else if p[len(p)-1] == '*' && p[len(p)-2] == '*' {
- p = p[:len(p)-2]
- } else {
- p = p + "/"
- }
- if strings.HasPrefix(trimmedFile, p) {
- return strings.TrimPrefix(trimmedFile, p)
- }
- }
- return trimmedFile
- }
- func (config *Configuration) logf(fmt string, args ...interface{}) {
- if config != nil && config.Logger != nil {
- config.Logger.Printf(fmt, args...)
- } else {
- log.Printf(fmt, args...)
- }
- }
- func (config *Configuration) notifyInReleaseStage() bool {
- if config.NotifyReleaseStages == nil {
- return true
- }
- if config.ReleaseStage == "" {
- return true
- }
- for _, r := range config.NotifyReleaseStages {
- if r == config.ReleaseStage {
- return true
- }
- }
- return false
- }
|