Bugsnag Notifier for Golang =========================== The Bugsnag Notifier for Golang gives you instant notification of panics, or unexpected errors, in your golang app. Any unhandled panics will trigger a notification to be sent to your Bugsnag project. [Bugsnag](http://bugsnag.com) captures errors in real-time from your web, mobile and desktop applications, helping you to understand and resolve them as fast as possible. [Create a free account](http://bugsnag.com) to start capturing exceptions from your applications. ## How to Install 1. Download the code ```shell go get github.com/bugsnag/bugsnag-go ``` ### Using with net/http apps For a golang app based on [net/http](https://godoc.org/net/http), integrating Bugsnag takes two steps. You should also use these instructions if you're using the [gorilla toolkit](http://www.gorillatoolkit.org/), or the [pat](https://github.com/bmizerany/pat/) muxer. 1. Configure bugsnag at the start of your `main()` function: ```go import "github.com/bugsnag/bugsnag-go" func main() { bugsnag.Configure(bugsnag.Configuration{ APIKey: "YOUR_API_KEY_HERE", ReleaseStage: "production", // more configuration options }) // rest of your program. } ``` 2. Wrap your server in a [bugsnag.Handler](https://godoc.org/github.com/bugsnag/bugsnag-go/#Handler) ```go // a. If you're using the builtin http mux, you can just pass // bugsnag.Handler(nil) to http.ListenAndServer http.ListenAndServe(":8080", bugsnag.Handler(nil)) // b. If you're creating a server manually yourself, you can set // its handlers the same way srv := http.Server{ Handler: bugsnag.Handler(nil) } // c. If you're not using the builtin http mux, wrap your own handler // (though make sure that it doesn't already catch panics) http.ListenAndServe(":8080", bugsnag.Handler(handler)) ``` ### Using with Revel apps There are two steps to get panic handling in [revel](https://revel.github.io) apps. 1. Add the `bugsnagrevel.Filter` immediately after the `revel.PanicFilter` in `app/init.go`: ```go import "github.com/bugsnag/bugsnag-go/revel" revel.Filters = []revel.Filter{ revel.PanicFilter, bugsnagrevel.Filter, // ... } ``` 2. Set bugsnag.apikey in the top section of `conf/app.conf`. ``` module.static=github.com/revel/revel/modules/static bugsnag.apikey=YOUR_API_KEY_HERE [dev] ``` ### Using with Google App Engine 1. Configure bugsnag at the start of your `init()` function: ```go import "github.com/bugsnag/bugsnag-go" func init() { bugsnag.Configure(bugsnag.Configuration{ APIKey: "YOUR_API_KEY_HERE", }) // ... } ``` 2. Wrap *every* http.Handler or http.HandlerFunc with Bugsnag: ```go // a. If you're using HandlerFuncs http.HandleFunc("/", bugsnag.HandlerFunc( func (w http.ResponseWriter, r *http.Request) { // ... })) // b. If you're using Handlers http.Handle("/", bugsnag.Handler(myHttpHandler)) ``` 3. In order to use Bugsnag, you must provide the current [`appengine.Context`](https://developers.google.com/appengine/docs/go/reference#Context), or current `*http.Request` as rawData. The easiest way to do this is to create a new notifier. ```go c := appengine.NewContext(r) notifier := bugsnag.New(c) if err != nil { notifier.Notify(err) } go func () { defer notifier.Recover() // ... }() ``` ## Notifying Bugsnag manually Bugsnag will automatically handle any panics that crash your program and notify you of them. If you've integrated with `revel` or `net/http`, then you'll also be notified of any panics() that happen while processing a request. Sometimes however it's useful to manually notify Bugsnag of a problem. To do this, call [`bugsnag.Notify()`](https://godoc.org/github.com/bugsnag/bugsnag-go/#Notify) ```go if err != nil { bugsnag.Notify(err) } ``` ### Manual panic handling To avoid a panic in a goroutine from crashing your entire app, you can use [`bugsnag.Recover()`](https://godoc.org/github.com/bugsnag/bugsnag-go/#Recover) to stop a panic from unwinding the stack any further. When `Recover()` is hit, it will send any current panic to Bugsnag and then stop panicking. This is most useful at the start of a goroutine: ```go go func() { defer bugsnag.Recover() // ... }() ``` Alternatively you can use [`bugsnag.AutoNotify()`](https://godoc.org/github.com/bugsnag/bugsnag-go/#Recover) to notify bugsnag of a panic while letting the program continue to panic. This is useful if you're using a Framework that already has some handling of panics and you are retrofitting bugsnag support. ```go defer bugsnag.AutoNotify() ``` ## Sending Custom Data Most functions in the Bugsnag API, including `bugsnag.Notify()`, `bugsnag.Recover()`, `bugsnag.AutoNotify()`, and `bugsnag.Handler()` let you attach data to the notifications that they send. To do this you pass in rawData, which can be any of the supported types listed here. To add support for more types of rawData see [OnBeforeNotify](#custom-data-with-onbeforenotify). ### Custom MetaData Custom metaData appears as tabs on Bugsnag.com. You can set it by passing a [`bugsnag.MetaData`](https://godoc.org/github.com/bugsnag/bugsnag-go/#MetaData) object as rawData. ```go bugsnag.Notify(err, bugsnag.MetaData{ "Account": { "Name": Account.Name, "Paying": Account.Plan.Premium, }, }) ``` ### Request data Bugsnag can extract interesting data from [`*http.Request`](https://godoc.org/net/http/#Request) objects, and [`*revel.Controller`](https://godoc.org/github.com/revel/revel/#Controller) objects. These are automatically passed in when handling panics, and you can pass them yourself. ```go func (w http.ResponseWriter, r *http.Request) { bugsnag.Notify(err, r) } ``` ### User data User data is searchable, and the `Id` powers the count of users affected. You can set which user an error affects by passing a [`bugsnag.User`](https://godoc.org/github.com/bugsnag/bugsnag-go/#User) object as rawData. ```go bugsnag.Notify(err, bugsnag.User{Id: "1234", Name: "Conrad", Email: "me@cirw.in"}) ``` ### Context The context shows up prominently in the list view so that you can get an idea of where a problem occurred. You can set it by passing a [`bugsnag.Context`](https://godoc.org/github.com/bugsnag/bugsnag-go/#Context) object as rawData. ```go bugsnag.Notify(err, bugsnag.Context{"backgroundJob"}) ``` ### Severity Bugsnag supports three severities, `SeverityError`, `SeverityWarning`, and `SeverityInfo`. You can set the severity of an error by passing one of these objects as rawData. ```go bugsnag.Notify(err, bugsnag.SeverityInfo) ``` ## Configuration You must call `bugsnag.Configure()` at the start of your program to use Bugsnag, you pass it a [`bugsnag.Configuration`](https://godoc.org/github.com/bugsnag/bugsnag-go/#Configuration) object containing any of the following values. ### APIKey The Bugsnag API key can be found on your [Bugsnag dashboard](https://bugsnag.com) under "Settings". ```go bugsnag.Configure(bugsnag.Configuration{ APIKey: "YOUR_API_KEY_HERE", }) ``` ### Endpoint The Bugsnag endpoint defaults to `https://notify.bugsnag.com/`. If you're using Bugsnag enterprise, you should set this to the endpoint of your local instance. ```go bugsnag.Configure(bugsnag.Configuration{ Endpoint: "http://bugsnag.internal:49000/", }) ``` ### ReleaseStage The ReleaseStage tracks where your app is deployed. You should set this to `production`, `staging`, `development` or similar as appropriate. ```go bugsnag.Configure(bugsnag.Configuration{ ReleaseStage: "development", }) ``` ### NotifyReleaseStages The list of ReleaseStages to notify in. By default Bugsnag will notify you in all release stages, but you can use this to silence development errors. ```go bugsnag.Configure(bugsnag.Configuration{ NotifyReleaseStages: []string{"production", "staging"}, }) ``` ### AppVersion If you use a versioning scheme for deploys of your app, Bugsnag can use the `AppVersion` to only re-open errors if they occur in later version of the app. ```go bugsnag.Configure(bugsnag.Configuration{ AppVersion: "1.2.3", }) ``` ### Hostname The hostname is used to track where exceptions are coming from in the Bugsnag dashboard. The default value is obtained from `os.Hostname()` so you won't often need to change this. ```go bugsnag.Configure(bugsnag.Configuration{ Hostname: "go1", }) ``` ### ProjectPackages In order to determine where a crash happens Bugsnag needs to know which packages you consider to be part of your app (as opposed to a library). By default this is set to `[]string{"main*"}`. Strings are matched to package names using [`filepath.Match`](http://godoc.org/path/filepath#Match). ```go bugsnag.Configure(bugsnag.Configuration{ ProjectPackages: []string{"main", "github.com/domain/myapp/*"}, } ``` ### ParamsFilters Sometimes sensitive data is accidentally included in Bugsnag MetaData. You can remove it by setting `ParamsFilters`. Any key in the `MetaData` that includes any string in the filters will be redacted. The default is `[]string{"password", "secret"}`, which prevents fields like `password`, `password_confirmation` and `secret_answer` from being sent. ```go bugsnag.Configure(bugsnag.Configuration{ ParamsFilters: []string{"password", "secret"}, } ``` ### Logger The Logger to write to in case of an error inside Bugsnag. This defaults to the global logger. ```go bugsnag.Configure(bugsnag.Configuration{ Logger: app.Logger, } ``` ### PanicHandler The first time Bugsnag is configured, it wraps the running program in a panic handler using [panicwrap](http://godoc.org/github.com/ConradIrwin/panicwrap). This forks a sub-process which monitors unhandled panics. To prevent this, set `PanicHandler` to `func() {}` the first time you call `bugsnag.Configure`. This will prevent bugsnag from being able to notify you about unhandled panics. ```go bugsnag.Configure(bugsnag.Configuration{ PanicHandler: func() {}, }) ``` ### Synchronous Bugsnag usually starts a new goroutine before sending notifications. This means that notifications can be lost if you do a bugsnag.Notify and then immediately os.Exit. To avoid this problem, set Bugsnag to Synchronous (or just `panic()` instead ;). ```go bugsnag.Configure(bugsnag.Configuration{ Synchronous: true }) ``` Or just for one error: ```go bugsnag.Notify(err, bugsnag.Configuration{Synchronous: true}) ``` ### Transport The transport configures how Bugsnag makes http requests. By default we use [`http.DefaultTransport`](http://godoc.org/net/http#RoundTripper) which handles HTTP proxies automatically using the `$HTTP_PROXY` environment variable. ```go bugsnag.Configure(bugsnag.Configuration{ Transport: http.DefaultTransport, }) ``` ## Custom data with OnBeforeNotify While it's nice that you can pass `MetaData` directly into `bugsnag.Notify`, `bugsnag.AutoNotify`, and `bugsnag.Recover`, this can be a bit cumbersome and inefficient — you're constructing the meta-data whether or not it will actually be used. A better idea is to pass raw data in to these functions, and add an `OnBeforeNotify` filter that converts them into `MetaData`. For example, lets say our system processes jobs: ```go type Job struct{ Retry bool UserId string UserEmail string Name string Params map[string]string } ``` You can pass a job directly into Bugsnag.notify: ```go bugsnag.Notify(err, job) ``` And then add a filter to extract information from that job and attach it to the Bugsnag event: ```go bugsnag.OnBeforeNotify( func(event *bugsnag.Event, config *bugsnag.Configuration) error { // Search all the RawData for any *Job pointers that we're passed in // to bugsnag.Notify() and friends. for _, datum := range event.RawData { if job, ok := datum.(*Job); ok { // don't notify bugsnag about errors in retries if job.Retry { return fmt.Errorf("not notifying about retried jobs") } // add the job as a tab on Bugsnag.com event.MetaData.AddStruct("Job", job) // set the user correctly event.User = &User{Id: job.UserId, Email: job.UserEmail} } } // continue notifying as normal return nil }) ``` ## Advanced Usage If you want to have multiple different configurations around in one program, you can use `bugsnag.New()` to create multiple independent instances of Bugsnag. You can use these without calling `bugsnag.Configure()`, but bear in mind that until you call `bugsnag.Configure()` unhandled panics will not be sent to bugsnag. ```go notifier := bugsnag.New(bugsnag.Configuration{ APIKey: "YOUR_OTHER_API_KEY", }) ``` In fact any place that lets you pass in `rawData` also allows you to pass in configuration. For example to send http errors to one bugsnag project, you could do: ```go bugsnag.Handler(nil, bugsnag.Configuration{APIKey: "YOUR_OTHER_API_KEY"}) ``` ### GroupingHash If you need to override Bugsnag's grouping algorithm, you can set the `GroupingHash` in an `OnBeforeNotify`: ```go bugsnag.OnBeforeNotify( func (event *bugsnag.Event, config *bugsnag.Configuration) error { event.GroupingHash = calculateGroupingHash(event) return nil }) ```