Move to vendor
Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
parent
c8d8e7e357
commit
77e69b9cf3
1268 changed files with 34 additions and 24 deletions
13
vendor/github.com/bugsnag/bugsnag-go/.travis.yml
generated
vendored
Normal file
13
vendor/github.com/bugsnag/bugsnag-go/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
||||
|
||||
install:
|
||||
- go get github.com/bugsnag/panicwrap
|
||||
- go get github.com/bugsnag/osext
|
||||
- go get github.com/bitly/go-simplejson
|
||||
- go get github.com/revel/revel
|
20
vendor/github.com/bugsnag/bugsnag-go/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/bugsnag/bugsnag-go/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2014 Bugsnag
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
489
vendor/github.com/bugsnag/bugsnag-go/README.md
generated
vendored
Normal file
489
vendor/github.com/bugsnag/bugsnag-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,489 @@
|
|||
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
|
||||
})
|
||||
```
|
76
vendor/github.com/bugsnag/bugsnag-go/appengine.go
generated
vendored
Normal file
76
vendor/github.com/bugsnag/bugsnag-go/appengine.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
// +build appengine
|
||||
|
||||
package bugsnag
|
||||
|
||||
import (
|
||||
"appengine"
|
||||
"appengine/urlfetch"
|
||||
"appengine/user"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func defaultPanicHandler() {}
|
||||
|
||||
func init() {
|
||||
OnBeforeNotify(appengineMiddleware)
|
||||
}
|
||||
|
||||
func appengineMiddleware(event *Event, config *Configuration) (err error) {
|
||||
var c appengine.Context
|
||||
|
||||
for _, datum := range event.RawData {
|
||||
if r, ok := datum.(*http.Request); ok {
|
||||
c = appengine.NewContext(r)
|
||||
break
|
||||
} else if context, ok := datum.(appengine.Context); ok {
|
||||
c = context
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
return fmt.Errorf("No appengine context given")
|
||||
}
|
||||
|
||||
// You can only use the builtin http library if you pay for appengine,
|
||||
// so we use the appengine urlfetch service instead.
|
||||
config.Transport = &urlfetch.Transport{
|
||||
Context: c,
|
||||
}
|
||||
|
||||
// Anything written to stderr/stdout is discarded, so lets log to the request.
|
||||
config.Logger = log.New(appengineWriter{c}, config.Logger.Prefix(), config.Logger.Flags())
|
||||
|
||||
// Set the releaseStage appropriately
|
||||
if config.ReleaseStage == "" {
|
||||
if appengine.IsDevAppServer() {
|
||||
config.ReleaseStage = "development"
|
||||
} else {
|
||||
config.ReleaseStage = "production"
|
||||
}
|
||||
}
|
||||
|
||||
if event.User == nil {
|
||||
u := user.Current(c)
|
||||
if u != nil {
|
||||
event.User = &User{
|
||||
Id: u.ID,
|
||||
Email: u.Email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert an appengine.Context into an io.Writer so we can create a log.Logger.
|
||||
type appengineWriter struct {
|
||||
appengine.Context
|
||||
}
|
||||
|
||||
func (c appengineWriter) Write(b []byte) (int, error) {
|
||||
c.Warningf(string(b))
|
||||
return len(b), nil
|
||||
}
|
131
vendor/github.com/bugsnag/bugsnag-go/bugsnag.go
generated
vendored
Normal file
131
vendor/github.com/bugsnag/bugsnag-go/bugsnag.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"github.com/bugsnag/bugsnag-go/errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
// Fixes a bug with SHA-384 intermediate certs on some platforms.
|
||||
// - https://github.com/bugsnag/bugsnag-go/issues/9
|
||||
_ "crypto/sha512"
|
||||
)
|
||||
|
||||
// The current version of bugsnag-go.
|
||||
const VERSION = "1.0.2"
|
||||
|
||||
var once sync.Once
|
||||
var middleware middlewareStack
|
||||
|
||||
// The configuration for the default bugsnag notifier.
|
||||
var Config Configuration
|
||||
|
||||
var defaultNotifier = Notifier{&Config, nil}
|
||||
|
||||
// Configure Bugsnag. The only required setting is the APIKey, which can be
|
||||
// obtained by clicking on "Settings" in your Bugsnag dashboard. This function
|
||||
// is also responsible for installing the global panic handler, so it should be
|
||||
// called as early as possible in your initialization process.
|
||||
func Configure(config Configuration) {
|
||||
Config.update(&config)
|
||||
once.Do(Config.PanicHandler)
|
||||
}
|
||||
|
||||
// Notify sends an error to Bugsnag along with the current stack trace. The
|
||||
// rawData is used to send extra information along with the error. For example
|
||||
// you can pass the current http.Request to Bugsnag to see information about it
|
||||
// in the dashboard, or set the severity of the notification.
|
||||
func Notify(err error, rawData ...interface{}) error {
|
||||
return defaultNotifier.Notify(errors.New(err, 1), rawData...)
|
||||
}
|
||||
|
||||
// AutoNotify logs a panic on a goroutine and then repanics.
|
||||
// It should only be used in places that have existing panic handlers further
|
||||
// up the stack. See bugsnag.Recover(). The rawData is used to send extra
|
||||
// information along with any panics that are handled this way.
|
||||
// Usage: defer bugsnag.AutoNotify()
|
||||
func AutoNotify(rawData ...interface{}) {
|
||||
if err := recover(); err != nil {
|
||||
rawData = defaultNotifier.addDefaultSeverity(rawData, SeverityError)
|
||||
defaultNotifier.Notify(errors.New(err, 2), rawData...)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Recover logs a panic on a goroutine and then recovers.
|
||||
// The rawData is used to send extra information along with
|
||||
// any panics that are handled this way
|
||||
// Usage: defer bugsnag.Recover()
|
||||
func Recover(rawData ...interface{}) {
|
||||
if err := recover(); err != nil {
|
||||
rawData = defaultNotifier.addDefaultSeverity(rawData, SeverityWarning)
|
||||
defaultNotifier.Notify(errors.New(err, 2), rawData...)
|
||||
}
|
||||
}
|
||||
|
||||
// OnBeforeNotify adds a callback to be run before a notification is sent to
|
||||
// Bugsnag. It can be used to modify the event or its MetaData. Changes made
|
||||
// to the configuration are local to notifying about this event. To prevent the
|
||||
// event from being sent to Bugsnag return an error, this error will be
|
||||
// returned from bugsnag.Notify() and the event will not be sent.
|
||||
func OnBeforeNotify(callback func(event *Event, config *Configuration) error) {
|
||||
middleware.OnBeforeNotify(callback)
|
||||
}
|
||||
|
||||
// Handler creates an http Handler that notifies Bugsnag any panics that
|
||||
// happen. It then repanics so that the default http Server panic handler can
|
||||
// handle the panic too. The rawData is used to send extra information along
|
||||
// with any panics that are handled this way.
|
||||
func Handler(h http.Handler, rawData ...interface{}) http.Handler {
|
||||
notifier := New(rawData...)
|
||||
if h == nil {
|
||||
h = http.DefaultServeMux
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer notifier.AutoNotify(r)
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// HandlerFunc creates an http HandlerFunc that notifies Bugsnag about any
|
||||
// panics that happen. It then repanics so that the default http Server panic
|
||||
// handler can handle the panic too. The rawData is used to send extra
|
||||
// information along with any panics that are handled this way. If you have
|
||||
// already wrapped your http server using bugsnag.Handler() you don't also need
|
||||
// to wrap each HandlerFunc.
|
||||
func HandlerFunc(h http.HandlerFunc, rawData ...interface{}) http.HandlerFunc {
|
||||
notifier := New(rawData...)
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
defer notifier.AutoNotify(r)
|
||||
h(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Set up builtin middlewarez
|
||||
OnBeforeNotify(httpRequestMiddleware)
|
||||
|
||||
// Default configuration
|
||||
Config.update(&Configuration{
|
||||
APIKey: "",
|
||||
Endpoint: "https://notify.bugsnag.com/",
|
||||
Hostname: "",
|
||||
AppVersion: "",
|
||||
ReleaseStage: "",
|
||||
ParamsFilters: []string{"password", "secret"},
|
||||
// * for app-engine
|
||||
ProjectPackages: []string{"main*"},
|
||||
NotifyReleaseStages: nil,
|
||||
Logger: log.New(os.Stdout, log.Prefix(), log.Flags()),
|
||||
PanicHandler: defaultPanicHandler,
|
||||
Transport: http.DefaultTransport,
|
||||
})
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
Config.Hostname = hostname
|
||||
}
|
||||
}
|
461
vendor/github.com/bugsnag/bugsnag-go/bugsnag_test.go
generated
vendored
Normal file
461
vendor/github.com/bugsnag/bugsnag-go/bugsnag_test.go
generated
vendored
Normal file
|
@ -0,0 +1,461 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
func TestConfigure(t *testing.T) {
|
||||
Configure(Configuration{
|
||||
APIKey: testAPIKey,
|
||||
})
|
||||
|
||||
if Config.APIKey != testAPIKey {
|
||||
t.Errorf("Setting APIKey didn't work")
|
||||
}
|
||||
|
||||
if New().Config.APIKey != testAPIKey {
|
||||
t.Errorf("Setting APIKey didn't work for new notifiers")
|
||||
}
|
||||
}
|
||||
|
||||
var postedJSON = make(chan []byte, 10)
|
||||
var testOnce sync.Once
|
||||
var testEndpoint string
|
||||
var testAPIKey = "166f5ad3590596f9aa8d601ea89af845"
|
||||
|
||||
func startTestServer() {
|
||||
testOnce.Do(func() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
postedJSON <- body
|
||||
})
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testEndpoint = "http://" + l.Addr().String() + "/"
|
||||
|
||||
go http.Serve(l, mux)
|
||||
})
|
||||
}
|
||||
|
||||
type _recurse struct {
|
||||
*_recurse
|
||||
}
|
||||
|
||||
func TestNotify(t *testing.T) {
|
||||
startTestServer()
|
||||
|
||||
recurse := _recurse{}
|
||||
recurse._recurse = &recurse
|
||||
|
||||
OnBeforeNotify(func(event *Event, config *Configuration) error {
|
||||
if event.Context == "testing" {
|
||||
event.GroupingHash = "lol"
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
Notify(fmt.Errorf("hello world"),
|
||||
Configuration{
|
||||
APIKey: testAPIKey,
|
||||
Endpoint: testEndpoint,
|
||||
ReleaseStage: "test",
|
||||
AppVersion: "1.2.3",
|
||||
Hostname: "web1",
|
||||
ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"},
|
||||
},
|
||||
User{Id: "123", Name: "Conrad", Email: "me@cirw.in"},
|
||||
Context{"testing"},
|
||||
MetaData{"test": {
|
||||
"password": "sneaky",
|
||||
"value": "able",
|
||||
"broken": complex(1, 2),
|
||||
"recurse": recurse,
|
||||
}},
|
||||
)
|
||||
|
||||
json, err := simplejson.NewJson(<-postedJSON)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if json.Get("apiKey").MustString() != testAPIKey {
|
||||
t.Errorf("Wrong api key in payload")
|
||||
}
|
||||
|
||||
if json.GetPath("notifier", "name").MustString() != "Bugsnag Go" {
|
||||
t.Errorf("Wrong notifier name in payload")
|
||||
}
|
||||
|
||||
event := json.Get("events").GetIndex(0)
|
||||
|
||||
for k, value := range map[string]string{
|
||||
"payloadVersion": "2",
|
||||
"severity": "warning",
|
||||
"context": "testing",
|
||||
"groupingHash": "lol",
|
||||
"app.releaseStage": "test",
|
||||
"app.version": "1.2.3",
|
||||
"device.hostname": "web1",
|
||||
"user.id": "123",
|
||||
"user.name": "Conrad",
|
||||
"user.email": "me@cirw.in",
|
||||
"metaData.test.password": "[REDACTED]",
|
||||
"metaData.test.value": "able",
|
||||
"metaData.test.broken": "[complex128]",
|
||||
"metaData.test.recurse._recurse": "[RECURSION]",
|
||||
} {
|
||||
key := strings.Split(k, ".")
|
||||
if event.GetPath(key...).MustString() != value {
|
||||
t.Errorf("Wrong %v: %v != %v", key, event.GetPath(key...).MustString(), value)
|
||||
}
|
||||
}
|
||||
|
||||
exception := event.Get("exceptions").GetIndex(0)
|
||||
|
||||
if exception.Get("message").MustString() != "hello world" {
|
||||
t.Errorf("Wrong message in payload")
|
||||
}
|
||||
|
||||
if exception.Get("errorClass").MustString() != "*errors.errorString" {
|
||||
t.Errorf("Wrong errorClass in payload: %v", exception.Get("errorClass").MustString())
|
||||
}
|
||||
|
||||
frame0 := exception.Get("stacktrace").GetIndex(0)
|
||||
if frame0.Get("file").MustString() != "bugsnag_test.go" ||
|
||||
frame0.Get("method").MustString() != "TestNotify" ||
|
||||
frame0.Get("inProject").MustBool() != true ||
|
||||
frame0.Get("lineNumber").MustInt() == 0 {
|
||||
t.Errorf("Wrong frame0")
|
||||
}
|
||||
|
||||
frame1 := exception.Get("stacktrace").GetIndex(1)
|
||||
|
||||
if frame1.Get("file").MustString() != "testing/testing.go" ||
|
||||
frame1.Get("method").MustString() != "tRunner" ||
|
||||
frame1.Get("inProject").MustBool() != false ||
|
||||
frame1.Get("lineNumber").MustInt() == 0 {
|
||||
t.Errorf("Wrong frame1")
|
||||
}
|
||||
}
|
||||
|
||||
func crashyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
c := make(chan int)
|
||||
close(c)
|
||||
c <- 1
|
||||
}
|
||||
|
||||
func runCrashyServer(rawData ...interface{}) (net.Listener, error) {
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", crashyHandler)
|
||||
srv := http.Server{
|
||||
Addr: l.Addr().String(),
|
||||
Handler: Handler(mux, rawData...),
|
||||
ErrorLog: log.New(ioutil.Discard, log.Prefix(), 0),
|
||||
}
|
||||
|
||||
go srv.Serve(l)
|
||||
return l, err
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
startTestServer()
|
||||
|
||||
l, err := runCrashyServer(Configuration{
|
||||
APIKey: testAPIKey,
|
||||
Endpoint: testEndpoint,
|
||||
ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"},
|
||||
Logger: log.New(ioutil.Discard, log.Prefix(), log.Flags()),
|
||||
}, SeverityInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
http.Get("http://" + l.Addr().String() + "/ok?foo=bar")
|
||||
l.Close()
|
||||
|
||||
json, err := simplejson.NewJson(<-postedJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if json.Get("apiKey").MustString() != testAPIKey {
|
||||
t.Errorf("Wrong api key in payload")
|
||||
}
|
||||
|
||||
if json.GetPath("notifier", "name").MustString() != "Bugsnag Go" {
|
||||
t.Errorf("Wrong notifier name in payload")
|
||||
}
|
||||
|
||||
event := json.Get("events").GetIndex(0)
|
||||
|
||||
for k, value := range map[string]string{
|
||||
"payloadVersion": "2",
|
||||
"severity": "info",
|
||||
"user.id": "127.0.0.1",
|
||||
"metaData.Request.Url": "http://" + l.Addr().String() + "/ok?foo=bar",
|
||||
"metaData.Request.Method": "GET",
|
||||
} {
|
||||
key := strings.Split(k, ".")
|
||||
if event.GetPath(key...).MustString() != value {
|
||||
t.Errorf("Wrong %v: %v != %v", key, event.GetPath(key...).MustString(), value)
|
||||
}
|
||||
}
|
||||
|
||||
if event.GetPath("metaData", "Request", "Params", "foo").GetIndex(0).MustString() != "bar" {
|
||||
t.Errorf("missing GET params in request metadata")
|
||||
}
|
||||
|
||||
if event.GetPath("metaData", "Headers", "Accept-Encoding").GetIndex(0).MustString() != "gzip" {
|
||||
t.Errorf("missing GET params in request metadata: %v", event.GetPath("metaData", "Headers"))
|
||||
}
|
||||
|
||||
exception := event.Get("exceptions").GetIndex(0)
|
||||
|
||||
if exception.Get("message").MustString() != "runtime error: send on closed channel" {
|
||||
t.Errorf("Wrong message in payload: %v", exception.Get("message").MustString())
|
||||
}
|
||||
|
||||
if exception.Get("errorClass").MustString() != "runtime.errorCString" {
|
||||
t.Errorf("Wrong errorClass in payload: %v", exception.Get("errorClass").MustString())
|
||||
}
|
||||
|
||||
// TODO:CI these are probably dependent on go version.
|
||||
frame0 := exception.Get("stacktrace").GetIndex(0)
|
||||
if frame0.Get("file").MustString() != "runtime/panic.c" ||
|
||||
frame0.Get("method").MustString() != "panicstring" ||
|
||||
frame0.Get("inProject").MustBool() != false ||
|
||||
frame0.Get("lineNumber").MustInt() == 0 {
|
||||
t.Errorf("Wrong frame0: %v", frame0)
|
||||
}
|
||||
|
||||
frame3 := exception.Get("stacktrace").GetIndex(3)
|
||||
|
||||
if frame3.Get("file").MustString() != "bugsnag_test.go" ||
|
||||
frame3.Get("method").MustString() != "crashyHandler" ||
|
||||
frame3.Get("inProject").MustBool() != true ||
|
||||
frame3.Get("lineNumber").MustInt() == 0 {
|
||||
t.Errorf("Wrong frame3: %v", frame3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoNotify(t *testing.T) {
|
||||
|
||||
var panicked interface{}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
panicked = recover()
|
||||
}()
|
||||
defer AutoNotify(Configuration{Endpoint: testEndpoint, APIKey: testAPIKey})
|
||||
|
||||
panic("eggs")
|
||||
}()
|
||||
|
||||
if panicked.(string) != "eggs" {
|
||||
t.Errorf("didn't re-panic")
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(<-postedJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
event := json.Get("events").GetIndex(0)
|
||||
|
||||
if event.Get("severity").MustString() != "error" {
|
||||
t.Errorf("severity should be error")
|
||||
}
|
||||
exception := event.Get("exceptions").GetIndex(0)
|
||||
|
||||
if exception.Get("message").MustString() != "eggs" {
|
||||
t.Errorf("caught wrong panic")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecover(t *testing.T) {
|
||||
var panicked interface{}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
panicked = recover()
|
||||
}()
|
||||
defer Recover(Configuration{Endpoint: testEndpoint, APIKey: testAPIKey})
|
||||
|
||||
panic("ham")
|
||||
}()
|
||||
|
||||
if panicked != nil {
|
||||
t.Errorf("re-panick'd")
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(<-postedJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
event := json.Get("events").GetIndex(0)
|
||||
|
||||
if event.Get("severity").MustString() != "warning" {
|
||||
t.Errorf("severity should be warning")
|
||||
}
|
||||
exception := event.Get("exceptions").GetIndex(0)
|
||||
|
||||
if exception.Get("message").MustString() != "ham" {
|
||||
t.Errorf("caught wrong panic")
|
||||
}
|
||||
}
|
||||
|
||||
func handleGet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
var createAccount = handleGet
|
||||
|
||||
type _job struct {
|
||||
Name string
|
||||
Process func()
|
||||
}
|
||||
|
||||
func ExampleAutoNotify() interface{} {
|
||||
return func(w http.ResponseWriter, request *http.Request) {
|
||||
defer AutoNotify(request, Context{"createAccount"})
|
||||
|
||||
createAccount(w, request)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRecover(job _job) {
|
||||
go func() {
|
||||
defer Recover(Context{job.Name}, SeverityWarning)
|
||||
|
||||
job.Process()
|
||||
}()
|
||||
}
|
||||
|
||||
func ExampleConfigure() {
|
||||
Configure(Configuration{
|
||||
APIKey: "YOUR_API_KEY_HERE",
|
||||
|
||||
ReleaseStage: "production",
|
||||
|
||||
// See Configuration{} for other fields
|
||||
})
|
||||
}
|
||||
|
||||
func ExampleHandler() {
|
||||
// Set up your http handlers as usual
|
||||
http.HandleFunc("/", handleGet)
|
||||
|
||||
// use bugsnag.Handler(nil) to wrap the default http handlers
|
||||
// so that Bugsnag is automatically notified about panics.
|
||||
http.ListenAndServe(":1234", Handler(nil))
|
||||
}
|
||||
|
||||
func ExampleHandler_customServer() {
|
||||
// If you're using a custom server, set the handlers explicitly.
|
||||
http.HandleFunc("/", handleGet)
|
||||
|
||||
srv := http.Server{
|
||||
Addr: ":1234",
|
||||
ReadTimeout: 10 * time.Second,
|
||||
// use bugsnag.Handler(nil) to wrap the default http handlers
|
||||
// so that Bugsnag is automatically notified about panics.
|
||||
Handler: Handler(nil),
|
||||
}
|
||||
srv.ListenAndServe()
|
||||
}
|
||||
|
||||
func ExampleHandler_customHandlers() {
|
||||
// If you're using custom handlers, wrap the handlers explicitly.
|
||||
handler := http.NewServeMux()
|
||||
http.HandleFunc("/", handleGet)
|
||||
// use bugsnag.Handler(handler) to wrap the handlers so that Bugsnag is
|
||||
// automatically notified about panics
|
||||
http.ListenAndServe(":1234", Handler(handler))
|
||||
}
|
||||
|
||||
func ExampleNotify() {
|
||||
_, err := net.Listen("tcp", ":80")
|
||||
|
||||
if err != nil {
|
||||
Notify(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNotify_details(userID string) {
|
||||
_, err := net.Listen("tcp", ":80")
|
||||
|
||||
if err != nil {
|
||||
Notify(err,
|
||||
// show as low-severity
|
||||
SeverityInfo,
|
||||
// set the context
|
||||
Context{"createlistener"},
|
||||
// pass the user id in to count users affected.
|
||||
User{Id: userID},
|
||||
// custom meta-data tab
|
||||
MetaData{
|
||||
"Listen": {
|
||||
"Protocol": "tcp",
|
||||
"Port": "80",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Retry bool
|
||||
UserId string
|
||||
UserEmail string
|
||||
Name string
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
func ExampleOnBeforeNotify() {
|
||||
OnBeforeNotify(func(event *Event, config *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("bugsnag middleware: not notifying about job retry")
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
159
vendor/github.com/bugsnag/bugsnag-go/configuration.go
generated
vendored
Normal file
159
vendor/github.com/bugsnag/bugsnag-go/configuration.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// The current release stage. This defaults to "production" and is used to
|
||||
// filter errors in the Bugsnag dashboard.
|
||||
ReleaseStage 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
|
||||
// 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
|
||||
|
||||
// Any meta-data that matches these filters will be marked as [REDACTED]
|
||||
// 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 *log.Logger
|
||||
// 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
|
||||
// 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.Endpoint != "" {
|
||||
config.Endpoint = other.Endpoint
|
||||
}
|
||||
if other.Hostname != "" {
|
||||
config.Hostname = other.Hostname
|
||||
}
|
||||
if other.AppVersion != "" {
|
||||
config.AppVersion = other.AppVersion
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
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 match, _ := filepath.Match(p, pkg); match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (config *Configuration) stripProjectPackages(file string) string {
|
||||
for _, p := range config.ProjectPackages {
|
||||
if len(p) > 2 && p[len(p)-2] == '/' && p[len(p)-1] == '*' {
|
||||
p = p[:len(p)-1]
|
||||
} else {
|
||||
p = p + "/"
|
||||
}
|
||||
if strings.HasPrefix(file, p) {
|
||||
return strings.TrimPrefix(file, p)
|
||||
}
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
func (config *Configuration) log(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
|
||||
}
|
||||
for _, r := range config.NotifyReleaseStages {
|
||||
if r == config.ReleaseStage {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
58
vendor/github.com/bugsnag/bugsnag-go/configuration_test.go
generated
vendored
Normal file
58
vendor/github.com/bugsnag/bugsnag-go/configuration_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNotifyReleaseStages(t *testing.T) {
|
||||
|
||||
var testCases = []struct {
|
||||
stage string
|
||||
configured []string
|
||||
notify bool
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
stage: "production",
|
||||
notify: true,
|
||||
msg: "Should notify in all release stages by default",
|
||||
},
|
||||
{
|
||||
stage: "production",
|
||||
configured: []string{"development", "production"},
|
||||
notify: true,
|
||||
msg: "Failed to notify in configured release stage",
|
||||
},
|
||||
{
|
||||
stage: "staging",
|
||||
configured: []string{"development", "production"},
|
||||
notify: false,
|
||||
msg: "Failed to prevent notification in excluded release stage",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
Configure(Configuration{ReleaseStage: testCase.stage, NotifyReleaseStages: testCase.configured})
|
||||
|
||||
if Config.notifyInReleaseStage() != testCase.notify {
|
||||
t.Error(testCase.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectPackages(t *testing.T) {
|
||||
Configure(Configuration{ProjectPackages: []string{"main", "github.com/ConradIrwin/*"}})
|
||||
if !Config.isProjectPackage("main") {
|
||||
t.Error("literal project package doesn't work")
|
||||
}
|
||||
if !Config.isProjectPackage("github.com/ConradIrwin/foo") {
|
||||
t.Error("wildcard project package doesn't work")
|
||||
}
|
||||
if Config.isProjectPackage("runtime") {
|
||||
t.Error("wrong packges being marked in project")
|
||||
}
|
||||
if Config.isProjectPackage("github.com/ConradIrwin/foo/bar") {
|
||||
t.Error("wrong packges being marked in project")
|
||||
}
|
||||
|
||||
}
|
69
vendor/github.com/bugsnag/bugsnag-go/doc.go
generated
vendored
Normal file
69
vendor/github.com/bugsnag/bugsnag-go/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Package bugsnag captures errors in real-time and reports them to Bugsnag (http://bugsnag.com).
|
||||
|
||||
Using bugsnag-go is a three-step process.
|
||||
|
||||
1. As early as possible in your program configure the notifier with your APIKey. This sets up
|
||||
handling of panics that would otherwise crash your app.
|
||||
|
||||
func init() {
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
APIKey: "YOUR_API_KEY_HERE",
|
||||
})
|
||||
}
|
||||
|
||||
2. Add bugsnag to places that already catch panics. For example you should add it to the HTTP server
|
||||
when you call ListenAndServer:
|
||||
|
||||
http.ListenAndServe(":8080", bugsnag.Handler(nil))
|
||||
|
||||
If that's not possible, for example because you're using Google App Engine, you can also wrap each
|
||||
HTTP handler manually:
|
||||
|
||||
http.HandleFunc("/" bugsnag.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
||||
...
|
||||
})
|
||||
|
||||
3. To notify Bugsnag of an error that is not a panic, pass it to bugsnag.Notify. This will also
|
||||
log the error message using the configured Logger.
|
||||
|
||||
if err != nil {
|
||||
bugsnag.Notify(err)
|
||||
}
|
||||
|
||||
For detailed integration instructions see https://bugsnag.com/docs/notifiers/go.
|
||||
|
||||
Configuration
|
||||
|
||||
The only required configuration is the Bugsnag API key which can be obtained by clicking "Settings"
|
||||
on the top of https://bugsnag.com/ after signing up. We also recommend you set the ReleaseStage
|
||||
and AppVersion if these make sense for your deployment workflow.
|
||||
|
||||
RawData
|
||||
|
||||
If you need to attach extra data to Bugsnag notifications you can do that using
|
||||
the rawData mechanism. Most of the functions that send errors to Bugsnag allow
|
||||
you to pass in any number of interface{} values as rawData. The rawData can
|
||||
consist of the Severity, Context, User or MetaData types listed below, and
|
||||
there is also builtin support for *http.Requests.
|
||||
|
||||
bugsnag.Notify(err, bugsnag.SeverityError)
|
||||
|
||||
If you want to add custom tabs to your bugsnag dashboard you can pass any value in as rawData,
|
||||
and then process it into the event's metadata using a bugsnag.OnBeforeNotify() hook.
|
||||
|
||||
bugsnag.Notify(err, account)
|
||||
|
||||
bugsnag.OnBeforeNotify(func (e *bugsnag.Event, c *bugsnag.Configuration) {
|
||||
for datum := range e.RawData {
|
||||
if account, ok := datum.(Account); ok {
|
||||
e.MetaData.Add("account", "name", account.Name)
|
||||
e.MetaData.Add("account", "url", account.URL)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
If necessary you can pass Configuration in as rawData, or modify the Configuration object passed
|
||||
into OnBeforeNotify hooks. Configuration passed in this way only affects the current notification.
|
||||
*/
|
||||
package bugsnag
|
6
vendor/github.com/bugsnag/bugsnag-go/errors/README.md
generated
vendored
Normal file
6
vendor/github.com/bugsnag/bugsnag-go/errors/README.md
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
Adds stacktraces to errors in golang.
|
||||
|
||||
This was made to help build the Bugsnag notifier but can be used standalone if
|
||||
you like to have stacktraces on errors.
|
||||
|
||||
See [Godoc](https://godoc.org/github.com/bugsnag/bugsnag-go/errors) for the API docs.
|
90
vendor/github.com/bugsnag/bugsnag-go/errors/error.go
generated
vendored
Normal file
90
vendor/github.com/bugsnag/bugsnag-go/errors/error.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Package errors provides errors that have stack-traces.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// The maximum number of stackframes on any error.
|
||||
var MaxStackDepth = 50
|
||||
|
||||
// Error is an error with an attached stacktrace. It can be used
|
||||
// wherever the builtin error interface is expected.
|
||||
type Error struct {
|
||||
Err error
|
||||
stack []uintptr
|
||||
frames []StackFrame
|
||||
}
|
||||
|
||||
// New makes an Error from the given value. If that value is already an
|
||||
// error then it will be used directly, if not, it will be passed to
|
||||
// fmt.Errorf("%v"). The skip parameter indicates how far up the stack
|
||||
// to start the stacktrace. 0 is from the current call, 1 from its caller, etc.
|
||||
func New(e interface{}, skip int) *Error {
|
||||
var err error
|
||||
|
||||
switch e := e.(type) {
|
||||
case *Error:
|
||||
return e
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
|
||||
stack := make([]uintptr, MaxStackDepth)
|
||||
length := runtime.Callers(2+skip, stack[:])
|
||||
return &Error{
|
||||
Err: err,
|
||||
stack: stack[:length],
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf creates a new error with the given message. You can use it
|
||||
// as a drop-in replacement for fmt.Errorf() to provide descriptive
|
||||
// errors in return values.
|
||||
func Errorf(format string, a ...interface{}) *Error {
|
||||
return New(fmt.Errorf(format, a...), 1)
|
||||
}
|
||||
|
||||
// Error returns the underlying error's message.
|
||||
func (err *Error) Error() string {
|
||||
return err.Err.Error()
|
||||
}
|
||||
|
||||
// Stack returns the callstack formatted the same way that go does
|
||||
// in runtime/debug.Stack()
|
||||
func (err *Error) Stack() []byte {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
for _, frame := range err.StackFrames() {
|
||||
buf.WriteString(frame.String())
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// StackFrames returns an array of frames containing information about the
|
||||
// stack.
|
||||
func (err *Error) StackFrames() []StackFrame {
|
||||
if err.frames == nil {
|
||||
err.frames = make([]StackFrame, len(err.stack))
|
||||
|
||||
for i, pc := range err.stack {
|
||||
err.frames[i] = NewStackFrame(pc)
|
||||
}
|
||||
}
|
||||
|
||||
return err.frames
|
||||
}
|
||||
|
||||
// TypeName returns the type this error. e.g. *errors.stringError.
|
||||
func (err *Error) TypeName() string {
|
||||
if _, ok := err.Err.(uncaughtPanic); ok {
|
||||
return "panic"
|
||||
}
|
||||
return reflect.TypeOf(err.Err).String()
|
||||
}
|
117
vendor/github.com/bugsnag/bugsnag-go/errors/error_test.go
generated
vendored
Normal file
117
vendor/github.com/bugsnag/bugsnag-go/errors/error_test.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStackFormatMatches(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != 'a' {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bs := [][]byte{Errorf("hi").Stack(), debug.Stack()}
|
||||
|
||||
// Ignore the first line (as it contains the PC of the .Stack() call)
|
||||
bs[0] = bytes.SplitN(bs[0], []byte("\n"), 2)[1]
|
||||
bs[1] = bytes.SplitN(bs[1], []byte("\n"), 2)[1]
|
||||
|
||||
if bytes.Compare(bs[0], bs[1]) != 0 {
|
||||
t.Errorf("Stack didn't match")
|
||||
t.Errorf("%s", bs[0])
|
||||
t.Errorf("%s", bs[1])
|
||||
}
|
||||
}()
|
||||
|
||||
a()
|
||||
}
|
||||
|
||||
func TestSkipWorks(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != 'a' {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bs := [][]byte{New("hi", 2).Stack(), debug.Stack()}
|
||||
|
||||
// should skip four lines of debug.Stack()
|
||||
bs[1] = bytes.SplitN(bs[1], []byte("\n"), 5)[4]
|
||||
|
||||
if bytes.Compare(bs[0], bs[1]) != 0 {
|
||||
t.Errorf("Stack didn't match")
|
||||
t.Errorf("%s", bs[0])
|
||||
t.Errorf("%s", bs[1])
|
||||
}
|
||||
}()
|
||||
|
||||
a()
|
||||
}
|
||||
|
||||
func TestNewError(t *testing.T) {
|
||||
|
||||
e := func() error {
|
||||
return New("hi", 1)
|
||||
}()
|
||||
|
||||
if e.Error() != "hi" {
|
||||
t.Errorf("Constructor with a string failed")
|
||||
}
|
||||
|
||||
if New(fmt.Errorf("yo"), 0).Error() != "yo" {
|
||||
t.Errorf("Constructor with an error failed")
|
||||
}
|
||||
|
||||
if New(e, 0) != e {
|
||||
t.Errorf("Constructor with an Error failed")
|
||||
}
|
||||
|
||||
if New(nil, 0).Error() != "<nil>" {
|
||||
t.Errorf("Constructor with nil failed")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleErrorf(x int) (int, error) {
|
||||
if x%2 == 1 {
|
||||
return 0, Errorf("can only halve even numbers, got %d", x)
|
||||
}
|
||||
return x / 2, nil
|
||||
}
|
||||
|
||||
func ExampleNewError() (error, error) {
|
||||
// Wrap io.EOF with the current stack-trace and return it
|
||||
return nil, New(io.EOF, 0)
|
||||
}
|
||||
|
||||
func ExampleNewError_skip() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// skip 1 frame (the deferred function) and then return the wrapped err
|
||||
err = New(err, 1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func ExampleError_Stack(err Error) {
|
||||
fmt.Printf("Error: %s\n%s", err.Error(), err.Stack())
|
||||
}
|
||||
|
||||
func a() error {
|
||||
b(5)
|
||||
return nil
|
||||
}
|
||||
|
||||
func b(i int) {
|
||||
c()
|
||||
}
|
||||
|
||||
func c() {
|
||||
panic('a')
|
||||
}
|
127
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic.go
generated
vendored
Normal file
127
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type uncaughtPanic struct{ message string }
|
||||
|
||||
func (p uncaughtPanic) Error() string {
|
||||
return p.message
|
||||
}
|
||||
|
||||
// ParsePanic allows you to get an error object from the output of a go program
|
||||
// that panicked. This is particularly useful with https://github.com/mitchellh/panicwrap.
|
||||
func ParsePanic(text string) (*Error, error) {
|
||||
lines := strings.Split(text, "\n")
|
||||
|
||||
state := "start"
|
||||
|
||||
var message string
|
||||
var stack []StackFrame
|
||||
|
||||
for i := 0; i < len(lines); i++ {
|
||||
line := lines[i]
|
||||
|
||||
if state == "start" {
|
||||
if strings.HasPrefix(line, "panic: ") {
|
||||
message = strings.TrimPrefix(line, "panic: ")
|
||||
state = "seek"
|
||||
} else {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (no prefix): %s", line)
|
||||
}
|
||||
|
||||
} else if state == "seek" {
|
||||
if strings.HasPrefix(line, "goroutine ") && strings.HasSuffix(line, "[running]:") {
|
||||
state = "parsing"
|
||||
}
|
||||
|
||||
} else if state == "parsing" {
|
||||
if line == "" {
|
||||
state = "done"
|
||||
break
|
||||
}
|
||||
createdBy := false
|
||||
if strings.HasPrefix(line, "created by ") {
|
||||
line = strings.TrimPrefix(line, "created by ")
|
||||
createdBy = true
|
||||
}
|
||||
|
||||
i++
|
||||
|
||||
if i >= len(lines) {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (unpaired): %s", line)
|
||||
}
|
||||
|
||||
frame, err := parsePanicFrame(line, lines[i], createdBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stack = append(stack, *frame)
|
||||
if createdBy {
|
||||
state = "done"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state == "done" || state == "parsing" {
|
||||
return &Error{Err: uncaughtPanic{message}, frames: stack}, nil
|
||||
}
|
||||
return nil, Errorf("could not parse panic: %v", text)
|
||||
}
|
||||
|
||||
// The lines we're passing look like this:
|
||||
//
|
||||
// main.(*foo).destruct(0xc208067e98)
|
||||
// /0/go/src/github.com/bugsnag/bugsnag-go/pan/main.go:22 +0x151
|
||||
func parsePanicFrame(name string, line string, createdBy bool) (*StackFrame, error) {
|
||||
idx := strings.LastIndex(name, "(")
|
||||
if idx == -1 && !createdBy {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (no call): %s", name)
|
||||
}
|
||||
if idx != -1 {
|
||||
name = name[:idx]
|
||||
}
|
||||
pkg := ""
|
||||
|
||||
if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
|
||||
pkg += name[:lastslash] + "/"
|
||||
name = name[lastslash+1:]
|
||||
}
|
||||
if period := strings.Index(name, "."); period >= 0 {
|
||||
pkg += name[:period]
|
||||
name = name[period+1:]
|
||||
}
|
||||
|
||||
name = strings.Replace(name, "·", ".", -1)
|
||||
|
||||
if !strings.HasPrefix(line, "\t") {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (no tab): %s", line)
|
||||
}
|
||||
|
||||
idx = strings.LastIndex(line, ":")
|
||||
if idx == -1 {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (no line number): %s", line)
|
||||
}
|
||||
file := line[1:idx]
|
||||
|
||||
number := line[idx+1:]
|
||||
if idx = strings.Index(number, " +"); idx > -1 {
|
||||
number = number[:idx]
|
||||
}
|
||||
|
||||
lno, err := strconv.ParseInt(number, 10, 32)
|
||||
if err != nil {
|
||||
return nil, Errorf("bugsnag.panicParser: Invalid line (bad line number): %s", line)
|
||||
}
|
||||
|
||||
return &StackFrame{
|
||||
File: file,
|
||||
LineNumber: int(lno),
|
||||
Package: pkg,
|
||||
Name: name,
|
||||
}, nil
|
||||
}
|
142
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic_test.go
generated
vendored
Normal file
142
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic_test.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var createdBy = `panic: hello!
|
||||
|
||||
goroutine 54 [running]:
|
||||
runtime.panic(0x35ce40, 0xc208039db0)
|
||||
/0/c/go/src/pkg/runtime/panic.c:279 +0xf5
|
||||
github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers.func·001()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go:13 +0x74
|
||||
net/http.(*Server).Serve(0xc20806c780, 0x910c88, 0xc20803e168, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/http/server.go:1698 +0x91
|
||||
created by github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers.App.Index
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go:14 +0x3e
|
||||
|
||||
goroutine 16 [IO wait]:
|
||||
net.runtime_pollWait(0x911c30, 0x72, 0x0)
|
||||
/0/c/go/src/pkg/runtime/netpoll.goc:146 +0x66
|
||||
net.(*pollDesc).Wait(0xc2080ba990, 0x72, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:84 +0x46
|
||||
net.(*pollDesc).WaitRead(0xc2080ba990, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:89 +0x42
|
||||
net.(*netFD).accept(0xc2080ba930, 0x58be30, 0x0, 0x9103f0, 0x23)
|
||||
/0/c/go/src/pkg/net/fd_unix.go:409 +0x343
|
||||
net.(*TCPListener).AcceptTCP(0xc20803e168, 0x8, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:234 +0x5d
|
||||
net.(*TCPListener).Accept(0xc20803e168, 0x0, 0x0, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:244 +0x4b
|
||||
github.com/revel/revel.Run(0xe6d9)
|
||||
/0/go/src/github.com/revel/revel/server.go:113 +0x926
|
||||
main.main()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/tmp/main.go:109 +0xe1a
|
||||
`
|
||||
|
||||
var normalSplit = `panic: hello!
|
||||
|
||||
goroutine 54 [running]:
|
||||
runtime.panic(0x35ce40, 0xc208039db0)
|
||||
/0/c/go/src/pkg/runtime/panic.c:279 +0xf5
|
||||
github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers.func·001()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go:13 +0x74
|
||||
net/http.(*Server).Serve(0xc20806c780, 0x910c88, 0xc20803e168, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/http/server.go:1698 +0x91
|
||||
|
||||
goroutine 16 [IO wait]:
|
||||
net.runtime_pollWait(0x911c30, 0x72, 0x0)
|
||||
/0/c/go/src/pkg/runtime/netpoll.goc:146 +0x66
|
||||
net.(*pollDesc).Wait(0xc2080ba990, 0x72, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:84 +0x46
|
||||
net.(*pollDesc).WaitRead(0xc2080ba990, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:89 +0x42
|
||||
net.(*netFD).accept(0xc2080ba930, 0x58be30, 0x0, 0x9103f0, 0x23)
|
||||
/0/c/go/src/pkg/net/fd_unix.go:409 +0x343
|
||||
net.(*TCPListener).AcceptTCP(0xc20803e168, 0x8, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:234 +0x5d
|
||||
net.(*TCPListener).Accept(0xc20803e168, 0x0, 0x0, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:244 +0x4b
|
||||
github.com/revel/revel.Run(0xe6d9)
|
||||
/0/go/src/github.com/revel/revel/server.go:113 +0x926
|
||||
main.main()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/tmp/main.go:109 +0xe1a
|
||||
`
|
||||
|
||||
var lastGoroutine = `panic: hello!
|
||||
|
||||
goroutine 16 [IO wait]:
|
||||
net.runtime_pollWait(0x911c30, 0x72, 0x0)
|
||||
/0/c/go/src/pkg/runtime/netpoll.goc:146 +0x66
|
||||
net.(*pollDesc).Wait(0xc2080ba990, 0x72, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:84 +0x46
|
||||
net.(*pollDesc).WaitRead(0xc2080ba990, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/fd_poll_runtime.go:89 +0x42
|
||||
net.(*netFD).accept(0xc2080ba930, 0x58be30, 0x0, 0x9103f0, 0x23)
|
||||
/0/c/go/src/pkg/net/fd_unix.go:409 +0x343
|
||||
net.(*TCPListener).AcceptTCP(0xc20803e168, 0x8, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:234 +0x5d
|
||||
net.(*TCPListener).Accept(0xc20803e168, 0x0, 0x0, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/tcpsock_posix.go:244 +0x4b
|
||||
github.com/revel/revel.Run(0xe6d9)
|
||||
/0/go/src/github.com/revel/revel/server.go:113 +0x926
|
||||
main.main()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/tmp/main.go:109 +0xe1a
|
||||
|
||||
goroutine 54 [running]:
|
||||
runtime.panic(0x35ce40, 0xc208039db0)
|
||||
/0/c/go/src/pkg/runtime/panic.c:279 +0xf5
|
||||
github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers.func·001()
|
||||
/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go:13 +0x74
|
||||
net/http.(*Server).Serve(0xc20806c780, 0x910c88, 0xc20803e168, 0x0, 0x0)
|
||||
/0/c/go/src/pkg/net/http/server.go:1698 +0x91
|
||||
`
|
||||
|
||||
var result = []StackFrame{
|
||||
StackFrame{File: "/0/c/go/src/pkg/runtime/panic.c", LineNumber: 279, Name: "panic", Package: "runtime"},
|
||||
StackFrame{File: "/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go", LineNumber: 13, Name: "func.001", Package: "github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers"},
|
||||
StackFrame{File: "/0/c/go/src/pkg/net/http/server.go", LineNumber: 1698, Name: "(*Server).Serve", Package: "net/http"},
|
||||
}
|
||||
|
||||
var resultCreatedBy = append(result,
|
||||
StackFrame{File: "/0/go/src/github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers/app.go", LineNumber: 14, Name: "App.Index", Package: "github.com/loopj/bugsnag-example-apps/go/revelapp/app/controllers", ProgramCounter: 0x0})
|
||||
|
||||
func TestParsePanic(t *testing.T) {
|
||||
|
||||
todo := map[string]string{
|
||||
"createdBy": createdBy,
|
||||
"normalSplit": normalSplit,
|
||||
"lastGoroutine": lastGoroutine,
|
||||
}
|
||||
|
||||
for key, val := range todo {
|
||||
Err, err := ParsePanic(val)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if Err.TypeName() != "panic" {
|
||||
t.Errorf("Wrong type: %s", Err.TypeName())
|
||||
}
|
||||
|
||||
if Err.Error() != "hello!" {
|
||||
t.Errorf("Wrong message: %s", Err.TypeName())
|
||||
}
|
||||
|
||||
if Err.StackFrames()[0].Func() != nil {
|
||||
t.Errorf("Somehow managed to find a func...")
|
||||
}
|
||||
|
||||
result := result
|
||||
if key == "createdBy" {
|
||||
result = resultCreatedBy
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(Err.StackFrames(), result) {
|
||||
t.Errorf("Wrong stack for %s: %#v", key, Err.StackFrames())
|
||||
}
|
||||
}
|
||||
}
|
97
vendor/github.com/bugsnag/bugsnag-go/errors/stackframe.go
generated
vendored
Normal file
97
vendor/github.com/bugsnag/bugsnag-go/errors/stackframe.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A StackFrame contains all necessary information about to generate a line
|
||||
// in a callstack.
|
||||
type StackFrame struct {
|
||||
File string
|
||||
LineNumber int
|
||||
Name string
|
||||
Package string
|
||||
ProgramCounter uintptr
|
||||
}
|
||||
|
||||
// NewStackFrame popoulates a stack frame object from the program counter.
|
||||
func NewStackFrame(pc uintptr) (frame StackFrame) {
|
||||
|
||||
frame = StackFrame{ProgramCounter: pc}
|
||||
if frame.Func() == nil {
|
||||
return
|
||||
}
|
||||
frame.Package, frame.Name = packageAndName(frame.Func())
|
||||
|
||||
// pc -1 because the program counters we use are usually return addresses,
|
||||
// and we want to show the line that corresponds to the function call
|
||||
frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Func returns the function that this stackframe corresponds to
|
||||
func (frame *StackFrame) Func() *runtime.Func {
|
||||
if frame.ProgramCounter == 0 {
|
||||
return nil
|
||||
}
|
||||
return runtime.FuncForPC(frame.ProgramCounter)
|
||||
}
|
||||
|
||||
// String returns the stackframe formatted in the same way as go does
|
||||
// in runtime/debug.Stack()
|
||||
func (frame *StackFrame) String() string {
|
||||
str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter)
|
||||
|
||||
source, err := frame.SourceLine()
|
||||
if err != nil {
|
||||
return str
|
||||
}
|
||||
|
||||
return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source)
|
||||
}
|
||||
|
||||
// SourceLine gets the line of code (from File and Line) of the original source if possible
|
||||
func (frame *StackFrame) SourceLine() (string, error) {
|
||||
data, err := ioutil.ReadFile(frame.File)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lines := bytes.Split(data, []byte{'\n'})
|
||||
if frame.LineNumber <= 0 || frame.LineNumber >= len(lines) {
|
||||
return "???", nil
|
||||
}
|
||||
// -1 because line-numbers are 1 based, but our array is 0 based
|
||||
return string(bytes.Trim(lines[frame.LineNumber-1], " \t")), nil
|
||||
}
|
||||
|
||||
func packageAndName(fn *runtime.Func) (string, string) {
|
||||
name := fn.Name()
|
||||
pkg := ""
|
||||
|
||||
// The name includes the path name to the package, which is unnecessary
|
||||
// since the file name is already included. Plus, it has center dots.
|
||||
// That is, we see
|
||||
// runtime/debug.*T·ptrmethod
|
||||
// and want
|
||||
// *T.ptrmethod
|
||||
// Since the package path might contains dots (e.g. code.google.com/...),
|
||||
// we first remove the path prefix if there is one.
|
||||
if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
|
||||
pkg += name[:lastslash] + "/"
|
||||
name = name[lastslash+1:]
|
||||
}
|
||||
if period := strings.Index(name, "."); period >= 0 {
|
||||
pkg += name[:period]
|
||||
name = name[period+1:]
|
||||
}
|
||||
|
||||
name = strings.Replace(name, "·", ".", -1)
|
||||
return pkg, name
|
||||
}
|
134
vendor/github.com/bugsnag/bugsnag-go/event.go
generated
vendored
Normal file
134
vendor/github.com/bugsnag/bugsnag-go/event.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bugsnag/bugsnag-go/errors"
|
||||
)
|
||||
|
||||
// Context is the context of the error in Bugsnag.
|
||||
// This can be passed to Notify, Recover or AutoNotify as rawData.
|
||||
type Context struct {
|
||||
String string
|
||||
}
|
||||
|
||||
// User represents the searchable user-data on Bugsnag. The Id is also used
|
||||
// to determine the number of users affected by a bug. This can be
|
||||
// passed to Notify, Recover or AutoNotify as rawData.
|
||||
type User struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// Sets the severity of the error on Bugsnag. These values can be
|
||||
// passed to Notify, Recover or AutoNotify as rawData.
|
||||
var (
|
||||
SeverityError = severity{"error"}
|
||||
SeverityWarning = severity{"warning"}
|
||||
SeverityInfo = severity{"info"}
|
||||
)
|
||||
|
||||
// The severity tag type, private so that people can only use Error,Warning,Info
|
||||
type severity struct {
|
||||
String string
|
||||
}
|
||||
|
||||
// The form of stacktrace that Bugsnag expects
|
||||
type stackFrame struct {
|
||||
Method string `json:"method"`
|
||||
File string `json:"file"`
|
||||
LineNumber int `json:"lineNumber"`
|
||||
InProject bool `json:"inProject,omitempty"`
|
||||
}
|
||||
|
||||
// Event represents a payload of data that gets sent to Bugsnag.
|
||||
// This is passed to each OnBeforeNotify hook.
|
||||
type Event struct {
|
||||
|
||||
// The original error that caused this event, not sent to Bugsnag.
|
||||
Error *errors.Error
|
||||
|
||||
// The rawData affecting this error, not sent to Bugsnag.
|
||||
RawData []interface{}
|
||||
|
||||
// The error class to be sent to Bugsnag. This defaults to the type name of the Error, for
|
||||
// example *error.String
|
||||
ErrorClass string
|
||||
// The error message to be sent to Bugsnag. This defaults to the return value of Error.Error()
|
||||
Message string
|
||||
// The stacktrrace of the error to be sent to Bugsnag.
|
||||
Stacktrace []stackFrame
|
||||
|
||||
// The context to be sent to Bugsnag. This should be set to the part of the app that was running,
|
||||
// e.g. for http requests, set it to the path.
|
||||
Context string
|
||||
// The severity of the error. Can be SeverityError, SeverityWarning or SeverityInfo.
|
||||
Severity severity
|
||||
// The grouping hash is used to override Bugsnag's grouping. Set this if you'd like all errors with
|
||||
// the same grouping hash to group together in the dashboard.
|
||||
GroupingHash string
|
||||
|
||||
// User data to send to Bugsnag. This is searchable on the dashboard.
|
||||
User *User
|
||||
// Other MetaData to send to Bugsnag. Appears as a set of tabbed tables in the dashboard.
|
||||
MetaData MetaData
|
||||
}
|
||||
|
||||
func newEvent(err *errors.Error, rawData []interface{}, notifier *Notifier) (*Event, *Configuration) {
|
||||
|
||||
config := notifier.Config
|
||||
event := &Event{
|
||||
Error: err,
|
||||
RawData: append(notifier.RawData, rawData...),
|
||||
|
||||
ErrorClass: err.TypeName(),
|
||||
Message: err.Error(),
|
||||
Stacktrace: make([]stackFrame, len(err.StackFrames())),
|
||||
|
||||
Severity: SeverityWarning,
|
||||
|
||||
MetaData: make(MetaData),
|
||||
}
|
||||
|
||||
for _, datum := range event.RawData {
|
||||
switch datum := datum.(type) {
|
||||
case severity:
|
||||
event.Severity = datum
|
||||
|
||||
case Context:
|
||||
event.Context = datum.String
|
||||
|
||||
case Configuration:
|
||||
config = config.merge(&datum)
|
||||
|
||||
case MetaData:
|
||||
event.MetaData.Update(datum)
|
||||
|
||||
case User:
|
||||
event.User = &datum
|
||||
}
|
||||
}
|
||||
|
||||
for i, frame := range err.StackFrames() {
|
||||
file := frame.File
|
||||
inProject := config.isProjectPackage(frame.Package)
|
||||
|
||||
// remove $GOROOT and $GOHOME from other frames
|
||||
if idx := strings.Index(file, frame.Package); idx > -1 {
|
||||
file = file[idx:]
|
||||
}
|
||||
if inProject {
|
||||
file = config.stripProjectPackages(file)
|
||||
}
|
||||
|
||||
event.Stacktrace[i] = stackFrame{
|
||||
Method: frame.Name,
|
||||
File: file,
|
||||
LineNumber: frame.LineNumber,
|
||||
InProject: inProject,
|
||||
}
|
||||
}
|
||||
|
||||
return event, config
|
||||
}
|
43
vendor/github.com/bugsnag/bugsnag-go/json_tags.go
generated
vendored
Normal file
43
vendor/github.com/bugsnag/bugsnag-go/json_tags.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
// The code is stripped from:
|
||||
// http://golang.org/src/pkg/encoding/json/tags.go?m=text
|
||||
|
||||
package bugsnag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's "json"
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
185
vendor/github.com/bugsnag/bugsnag-go/metadata.go
generated
vendored
Normal file
185
vendor/github.com/bugsnag/bugsnag-go/metadata.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MetaData is added to the Bugsnag dashboard in tabs. Each tab is
|
||||
// a map of strings -> values. You can pass MetaData to Notify, Recover
|
||||
// and AutoNotify as rawData.
|
||||
type MetaData map[string]map[string]interface{}
|
||||
|
||||
// Update the meta-data with more information. Tabs are merged together such
|
||||
// that unique keys from both sides are preserved, and duplicate keys end up
|
||||
// with the provided values.
|
||||
func (meta MetaData) Update(other MetaData) {
|
||||
for name, tab := range other {
|
||||
|
||||
if meta[name] == nil {
|
||||
meta[name] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
for key, value := range tab {
|
||||
meta[name][key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add creates a tab of Bugsnag meta-data.
|
||||
// If the tab doesn't yet exist it will be created.
|
||||
// If the key already exists, it will be overwritten.
|
||||
func (meta MetaData) Add(tab string, key string, value interface{}) {
|
||||
if meta[tab] == nil {
|
||||
meta[tab] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
meta[tab][key] = value
|
||||
}
|
||||
|
||||
// AddStruct creates a tab of Bugsnag meta-data.
|
||||
// The struct will be converted to an Object using the
|
||||
// reflect library so any private fields will not be exported.
|
||||
// As a safety measure, if you pass a non-struct the value will be
|
||||
// sent to Bugsnag under the "Extra data" tab.
|
||||
func (meta MetaData) AddStruct(tab string, obj interface{}) {
|
||||
val := sanitizer{}.Sanitize(obj)
|
||||
content, ok := val.(map[string]interface{})
|
||||
if ok {
|
||||
meta[tab] = content
|
||||
} else {
|
||||
// Wasn't a struct
|
||||
meta.Add("Extra data", tab, obj)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remove any values from meta-data that have keys matching the filters,
|
||||
// and any that are recursive data-structures
|
||||
func (meta MetaData) sanitize(filters []string) interface{} {
|
||||
return sanitizer{
|
||||
Filters: filters,
|
||||
Seen: make([]interface{}, 0),
|
||||
}.Sanitize(meta)
|
||||
|
||||
}
|
||||
|
||||
// The sanitizer is used to remove filtered params and recursion from meta-data.
|
||||
type sanitizer struct {
|
||||
Filters []string
|
||||
Seen []interface{}
|
||||
}
|
||||
|
||||
func (s sanitizer) Sanitize(data interface{}) interface{} {
|
||||
for _, s := range s.Seen {
|
||||
// TODO: we don't need deep equal here, just type-ignoring equality
|
||||
if reflect.DeepEqual(data, s) {
|
||||
return "[RECURSION]"
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitizers are passed by value, so we can modify s and it only affects
|
||||
// s.Seen for nested calls.
|
||||
s.Seen = append(s.Seen, data)
|
||||
|
||||
t := reflect.TypeOf(data)
|
||||
v := reflect.ValueOf(data)
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return data
|
||||
|
||||
case reflect.String:
|
||||
return data
|
||||
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return s.Sanitize(v.Elem().Interface())
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
ret := make([]interface{}, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
ret[i] = s.Sanitize(v.Index(i).Interface())
|
||||
}
|
||||
return ret
|
||||
|
||||
case reflect.Map:
|
||||
return s.sanitizeMap(v)
|
||||
|
||||
case reflect.Struct:
|
||||
return s.sanitizeStruct(v, t)
|
||||
|
||||
// Things JSON can't serialize:
|
||||
// case t.Chan, t.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
|
||||
default:
|
||||
return "[" + t.String() + "]"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s sanitizer) sanitizeMap(v reflect.Value) interface{} {
|
||||
ret := make(map[string]interface{})
|
||||
|
||||
for _, key := range v.MapKeys() {
|
||||
val := s.Sanitize(v.MapIndex(key).Interface())
|
||||
newKey := fmt.Sprintf("%v", key.Interface())
|
||||
|
||||
if s.shouldRedact(newKey) {
|
||||
val = "[REDACTED]"
|
||||
}
|
||||
|
||||
ret[newKey] = val
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s sanitizer) sanitizeStruct(v reflect.Value, t reflect.Type) interface{} {
|
||||
ret := make(map[string]interface{})
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
|
||||
val := v.Field(i)
|
||||
// Don't export private fields
|
||||
if !val.CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
name := t.Field(i).Name
|
||||
var opts tagOptions
|
||||
|
||||
// Parse JSON tags. Supports name and "omitempty"
|
||||
if jsonTag := t.Field(i).Tag.Get("json"); len(jsonTag) != 0 {
|
||||
name, opts = parseTag(jsonTag)
|
||||
}
|
||||
|
||||
if s.shouldRedact(name) {
|
||||
ret[name] = "[REDACTED]"
|
||||
} else {
|
||||
sanitized := s.Sanitize(val.Interface())
|
||||
if str, ok := sanitized.(string); ok {
|
||||
if !(opts.Contains("omitempty") && len(str) == 0) {
|
||||
ret[name] = str
|
||||
}
|
||||
} else {
|
||||
ret[name] = sanitized
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s sanitizer) shouldRedact(key string) bool {
|
||||
for _, filter := range s.Filters {
|
||||
if strings.Contains(strings.ToLower(filter), strings.ToLower(key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
182
vendor/github.com/bugsnag/bugsnag-go/metadata_test.go
generated
vendored
Normal file
182
vendor/github.com/bugsnag/bugsnag-go/metadata_test.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bugsnag/bugsnag-go/errors"
|
||||
)
|
||||
|
||||
type _account struct {
|
||||
ID string
|
||||
Name string
|
||||
Plan struct {
|
||||
Premium bool
|
||||
}
|
||||
Password string
|
||||
secret string
|
||||
Email string `json:"email"`
|
||||
EmptyEmail string `json:"emptyemail,omitempty"`
|
||||
NotEmptyEmail string `json:"not_empty_email,omitempty"`
|
||||
}
|
||||
|
||||
type _broken struct {
|
||||
Me *_broken
|
||||
Data string
|
||||
}
|
||||
|
||||
var account = _account{}
|
||||
var notifier = New(Configuration{})
|
||||
|
||||
func TestMetaDataAdd(t *testing.T) {
|
||||
m := MetaData{
|
||||
"one": {
|
||||
"key": "value",
|
||||
"override": false,
|
||||
}}
|
||||
|
||||
m.Add("one", "override", true)
|
||||
m.Add("one", "new", "key")
|
||||
m.Add("new", "tab", account)
|
||||
|
||||
m.AddStruct("lol", "not really a struct")
|
||||
m.AddStruct("account", account)
|
||||
|
||||
if !reflect.DeepEqual(m, MetaData{
|
||||
"one": {
|
||||
"key": "value",
|
||||
"override": true,
|
||||
"new": "key",
|
||||
},
|
||||
"new": {
|
||||
"tab": account,
|
||||
},
|
||||
"Extra data": {
|
||||
"lol": "not really a struct",
|
||||
},
|
||||
"account": {
|
||||
"ID": "",
|
||||
"Name": "",
|
||||
"Plan": map[string]interface{}{
|
||||
"Premium": false,
|
||||
},
|
||||
"Password": "",
|
||||
"email": "",
|
||||
},
|
||||
}) {
|
||||
t.Errorf("metadata.Add didn't work: %#v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaDataUpdate(t *testing.T) {
|
||||
|
||||
m := MetaData{
|
||||
"one": {
|
||||
"key": "value",
|
||||
"override": false,
|
||||
}}
|
||||
|
||||
m.Update(MetaData{
|
||||
"one": {
|
||||
"override": true,
|
||||
"new": "key",
|
||||
},
|
||||
"new": {
|
||||
"tab": account,
|
||||
},
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(m, MetaData{
|
||||
"one": {
|
||||
"key": "value",
|
||||
"override": true,
|
||||
"new": "key",
|
||||
},
|
||||
"new": {
|
||||
"tab": account,
|
||||
},
|
||||
}) {
|
||||
t.Errorf("metadata.Update didn't work: %#v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaDataSanitize(t *testing.T) {
|
||||
|
||||
var broken = _broken{}
|
||||
broken.Me = &broken
|
||||
broken.Data = "ohai"
|
||||
account.Name = "test"
|
||||
account.ID = "test"
|
||||
account.secret = "hush"
|
||||
account.Email = "example@example.com"
|
||||
account.EmptyEmail = ""
|
||||
account.NotEmptyEmail = "not_empty_email@example.com"
|
||||
|
||||
m := MetaData{
|
||||
"one": {
|
||||
"bool": true,
|
||||
"int": 7,
|
||||
"float": 7.1,
|
||||
"complex": complex(1, 1),
|
||||
"func": func() {},
|
||||
"unsafe": unsafe.Pointer(broken.Me),
|
||||
"string": "string",
|
||||
"password": "secret",
|
||||
"array": []hash{{
|
||||
"creditcard": "1234567812345678",
|
||||
"broken": broken,
|
||||
}},
|
||||
"broken": broken,
|
||||
"account": account,
|
||||
},
|
||||
}
|
||||
|
||||
n := m.sanitize([]string{"password", "creditcard"})
|
||||
|
||||
if !reflect.DeepEqual(n, map[string]interface{}{
|
||||
"one": map[string]interface{}{
|
||||
"bool": true,
|
||||
"int": 7,
|
||||
"float": 7.1,
|
||||
"complex": "[complex128]",
|
||||
"string": "string",
|
||||
"unsafe": "[unsafe.Pointer]",
|
||||
"func": "[func()]",
|
||||
"password": "[REDACTED]",
|
||||
"array": []interface{}{map[string]interface{}{
|
||||
"creditcard": "[REDACTED]",
|
||||
"broken": map[string]interface{}{
|
||||
"Me": "[RECURSION]",
|
||||
"Data": "ohai",
|
||||
},
|
||||
}},
|
||||
"broken": map[string]interface{}{
|
||||
"Me": "[RECURSION]",
|
||||
"Data": "ohai",
|
||||
},
|
||||
"account": map[string]interface{}{
|
||||
"ID": "test",
|
||||
"Name": "test",
|
||||
"Plan": map[string]interface{}{
|
||||
"Premium": false,
|
||||
},
|
||||
"Password": "[REDACTED]",
|
||||
"email": "example@example.com",
|
||||
"not_empty_email": "not_empty_email@example.com",
|
||||
},
|
||||
},
|
||||
}) {
|
||||
t.Errorf("metadata.Sanitize didn't work: %#v", n)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ExampleMetaData() {
|
||||
notifier.Notify(errors.Errorf("hi world"),
|
||||
MetaData{"Account": {
|
||||
"id": account.ID,
|
||||
"name": account.Name,
|
||||
"paying?": account.Plan.Premium,
|
||||
}})
|
||||
}
|
96
vendor/github.com/bugsnag/bugsnag-go/middleware.go
generated
vendored
Normal file
96
vendor/github.com/bugsnag/bugsnag-go/middleware.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
beforeFunc func(*Event, *Configuration) error
|
||||
|
||||
// MiddlewareStacks keep middleware in the correct order. They are
|
||||
// called in reverse order, so if you add a new middleware it will
|
||||
// be called before all existing middleware.
|
||||
middlewareStack struct {
|
||||
before []beforeFunc
|
||||
}
|
||||
)
|
||||
|
||||
// AddMiddleware adds a new middleware to the outside of the existing ones,
|
||||
// when the middlewareStack is Run it will be run before all middleware that
|
||||
// have been added before.
|
||||
func (stack *middlewareStack) OnBeforeNotify(middleware beforeFunc) {
|
||||
stack.before = append(stack.before, middleware)
|
||||
}
|
||||
|
||||
// Run causes all the middleware to be run. If they all permit it the next callback
|
||||
// will be called with all the middleware on the stack.
|
||||
func (stack *middlewareStack) Run(event *Event, config *Configuration, next func() error) error {
|
||||
// run all the before filters in reverse order
|
||||
for i := range stack.before {
|
||||
before := stack.before[len(stack.before)-i-1]
|
||||
|
||||
err := stack.runBeforeFilter(before, event, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
|
||||
func (stack *middlewareStack) runBeforeFilter(f beforeFunc, event *Event, config *Configuration) error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
config.log("bugsnag/middleware: unexpected panic: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return f(event, config)
|
||||
}
|
||||
|
||||
// catchMiddlewarePanic is used to log any panics that happen inside Middleware,
|
||||
// we wouldn't want to not notify Bugsnag in this case.
|
||||
func catchMiddlewarePanic(event *Event, config *Configuration, next func() error) {
|
||||
}
|
||||
|
||||
// httpRequestMiddleware is added OnBeforeNotify by default. It takes information
|
||||
// from an http.Request passed in as rawData, and adds it to the Event. You can
|
||||
// use this as a template for writing your own Middleware.
|
||||
func httpRequestMiddleware(event *Event, config *Configuration) error {
|
||||
for _, datum := range event.RawData {
|
||||
if request, ok := datum.(*http.Request); ok {
|
||||
proto := "http://"
|
||||
if request.TLS != nil {
|
||||
proto = "https://"
|
||||
}
|
||||
|
||||
event.MetaData.Update(MetaData{
|
||||
"Request": {
|
||||
"RemoteAddr": request.RemoteAddr,
|
||||
"Method": request.Method,
|
||||
"Url": proto + request.Host + request.RequestURI,
|
||||
"Params": request.URL.Query(),
|
||||
},
|
||||
})
|
||||
|
||||
// Add headers as a separate tab.
|
||||
event.MetaData.AddStruct("Headers", request.Header)
|
||||
|
||||
// Default context to Path
|
||||
if event.Context == "" {
|
||||
event.Context = request.URL.Path
|
||||
}
|
||||
|
||||
// Default user.id to IP so that users-affected works.
|
||||
if event.User == nil {
|
||||
ip := request.RemoteAddr
|
||||
if idx := strings.LastIndex(ip, ":"); idx != -1 {
|
||||
ip = ip[:idx]
|
||||
}
|
||||
event.User = &User{Id: ip}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
88
vendor/github.com/bugsnag/bugsnag-go/middleware_test.go
generated
vendored
Normal file
88
vendor/github.com/bugsnag/bugsnag-go/middleware_test.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMiddlewareOrder(t *testing.T) {
|
||||
|
||||
result := make([]int, 0, 7)
|
||||
stack := middlewareStack{}
|
||||
stack.OnBeforeNotify(func(e *Event, c *Configuration) error {
|
||||
result = append(result, 2)
|
||||
return nil
|
||||
})
|
||||
stack.OnBeforeNotify(func(e *Event, c *Configuration) error {
|
||||
result = append(result, 1)
|
||||
return nil
|
||||
})
|
||||
stack.OnBeforeNotify(func(e *Event, c *Configuration) error {
|
||||
result = append(result, 0)
|
||||
return nil
|
||||
})
|
||||
|
||||
stack.Run(nil, nil, func() error {
|
||||
result = append(result, 3)
|
||||
return nil
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(result, []int{0, 1, 2, 3}) {
|
||||
t.Errorf("unexpected middleware order %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeforeNotifyReturnErr(t *testing.T) {
|
||||
|
||||
stack := middlewareStack{}
|
||||
err := fmt.Errorf("test")
|
||||
|
||||
stack.OnBeforeNotify(func(e *Event, c *Configuration) error {
|
||||
return err
|
||||
})
|
||||
|
||||
called := false
|
||||
|
||||
e := stack.Run(nil, nil, func() error {
|
||||
called = true
|
||||
return nil
|
||||
})
|
||||
|
||||
if e != err {
|
||||
t.Errorf("Middleware didn't return the error")
|
||||
}
|
||||
|
||||
if called == true {
|
||||
t.Errorf("Notify was called when BeforeNotify returned False")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeforeNotifyPanic(t *testing.T) {
|
||||
|
||||
stack := middlewareStack{}
|
||||
|
||||
stack.OnBeforeNotify(func(e *Event, c *Configuration) error {
|
||||
panic("oops")
|
||||
})
|
||||
|
||||
called := false
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
stack.Run(nil, &Configuration{Logger: log.New(b, log.Prefix(), 0)}, func() error {
|
||||
called = true
|
||||
return nil
|
||||
})
|
||||
|
||||
logged := b.String()
|
||||
|
||||
if logged != "bugsnag/middleware: unexpected panic: oops\n" {
|
||||
t.Errorf("Logged: %s", logged)
|
||||
}
|
||||
|
||||
if called == false {
|
||||
t.Errorf("Notify was not called when BeforeNotify panicked")
|
||||
}
|
||||
}
|
95
vendor/github.com/bugsnag/bugsnag-go/notifier.go
generated
vendored
Normal file
95
vendor/github.com/bugsnag/bugsnag-go/notifier.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bugsnag/bugsnag-go/errors"
|
||||
)
|
||||
|
||||
// Notifier sends errors to Bugsnag.
|
||||
type Notifier struct {
|
||||
Config *Configuration
|
||||
RawData []interface{}
|
||||
}
|
||||
|
||||
// New creates a new notifier.
|
||||
// You can pass an instance of bugsnag.Configuration in rawData to change the configuration.
|
||||
// Other values of rawData will be passed to Notify.
|
||||
func New(rawData ...interface{}) *Notifier {
|
||||
config := Config.clone()
|
||||
for i, datum := range rawData {
|
||||
if c, ok := datum.(Configuration); ok {
|
||||
config.update(&c)
|
||||
rawData[i] = nil
|
||||
}
|
||||
}
|
||||
|
||||
return &Notifier{
|
||||
Config: config,
|
||||
RawData: rawData,
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends an error to Bugsnag. Any rawData you pass here will be sent to
|
||||
// Bugsnag after being converted to JSON. e.g. bugsnag.SeverityError, bugsnag.Context,
|
||||
// or bugsnag.MetaData.
|
||||
func (notifier *Notifier) Notify(err error, rawData ...interface{}) (e error) {
|
||||
event, config := newEvent(errors.New(err, 1), rawData, notifier)
|
||||
|
||||
// Never block, start throwing away errors if we have too many.
|
||||
e = middleware.Run(event, config, func() error {
|
||||
config.log("notifying bugsnag: %s", event.Message)
|
||||
if config.notifyInReleaseStage() {
|
||||
if config.Synchronous {
|
||||
return (&payload{event, config}).deliver()
|
||||
}
|
||||
go (&payload{event, config}).deliver()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("not notifying in %s", config.ReleaseStage)
|
||||
})
|
||||
|
||||
if e != nil {
|
||||
config.log("bugsnag.Notify: %v", e)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// AutoNotify notifies Bugsnag of any panics, then repanics.
|
||||
// It sends along any rawData that gets passed in.
|
||||
// Usage: defer AutoNotify()
|
||||
func (notifier *Notifier) AutoNotify(rawData ...interface{}) {
|
||||
if err := recover(); err != nil {
|
||||
rawData = notifier.addDefaultSeverity(rawData, SeverityError)
|
||||
notifier.Notify(errors.New(err, 2), rawData...)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Recover logs any panics, then recovers.
|
||||
// It sends along any rawData that gets passed in.
|
||||
// Usage: defer Recover()
|
||||
func (notifier *Notifier) Recover(rawData ...interface{}) {
|
||||
if err := recover(); err != nil {
|
||||
rawData = notifier.addDefaultSeverity(rawData, SeverityWarning)
|
||||
notifier.Notify(errors.New(err, 2), rawData...)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifier *Notifier) dontPanic() {
|
||||
if err := recover(); err != nil {
|
||||
notifier.Config.log("bugsnag/notifier.Notify: panic! %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a severity to raw data only if the default is not set.
|
||||
func (notifier *Notifier) addDefaultSeverity(rawData []interface{}, s severity) []interface{} {
|
||||
|
||||
for _, datum := range append(notifier.RawData, rawData...) {
|
||||
if _, ok := datum.(severity); ok {
|
||||
return rawData
|
||||
}
|
||||
}
|
||||
|
||||
return append(rawData, s)
|
||||
}
|
27
vendor/github.com/bugsnag/bugsnag-go/panicwrap.go
generated
vendored
Normal file
27
vendor/github.com/bugsnag/bugsnag-go/panicwrap.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// +build !appengine
|
||||
|
||||
package bugsnag
|
||||
|
||||
import (
|
||||
"github.com/bugsnag/panicwrap"
|
||||
"github.com/bugsnag/bugsnag-go/errors"
|
||||
)
|
||||
|
||||
// NOTE: this function does not return when you call it, instead it
|
||||
// re-exec()s the current process with panic monitoring.
|
||||
func defaultPanicHandler() {
|
||||
defer defaultNotifier.dontPanic()
|
||||
|
||||
err := panicwrap.BasicMonitor(func(output string) {
|
||||
toNotify, err := errors.ParsePanic(output)
|
||||
|
||||
if err != nil {
|
||||
defaultNotifier.Config.log("bugsnag.handleUncaughtPanic: %v", err)
|
||||
}
|
||||
Notify(toNotify, SeverityError, Configuration{Synchronous: true})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
defaultNotifier.Config.log("bugsnag.handleUncaughtPanic: %v", err)
|
||||
}
|
||||
}
|
79
vendor/github.com/bugsnag/bugsnag-go/panicwrap_test.go
generated
vendored
Normal file
79
vendor/github.com/bugsnag/bugsnag-go/panicwrap_test.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
// +build !appengine
|
||||
|
||||
package bugsnag
|
||||
|
||||
import (
|
||||
"github.com/bitly/go-simplejson"
|
||||
"github.com/mitchellh/osext"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPanicHandler(t *testing.T) {
|
||||
startTestServer()
|
||||
|
||||
exePath, err := osext.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the same trick as panicwrap() to re-run ourselves.
|
||||
// In the init() block below, we will then panic.
|
||||
cmd := exec.Command(exePath, os.Args[1:]...)
|
||||
cmd.Env = append(os.Environ(), "BUGSNAG_API_KEY="+testAPIKey, "BUGSNAG_ENDPOINT="+testEndpoint, "please_panic=please_panic")
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = cmd.Wait(); err.Error() != "exit status 2" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(<-postedJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
event := json.Get("events").GetIndex(0)
|
||||
|
||||
if event.Get("severity").MustString() != "error" {
|
||||
t.Errorf("severity should be error")
|
||||
}
|
||||
exception := event.Get("exceptions").GetIndex(0)
|
||||
|
||||
if exception.Get("message").MustString() != "ruh roh" {
|
||||
t.Errorf("caught wrong panic")
|
||||
}
|
||||
|
||||
if exception.Get("errorClass").MustString() != "panic" {
|
||||
t.Errorf("caught wrong panic")
|
||||
}
|
||||
|
||||
frame := exception.Get("stacktrace").GetIndex(1)
|
||||
|
||||
// Yeah, we just caught a panic from the init() function below and sent it to the server running above (mindblown)
|
||||
if frame.Get("inProject").MustBool() != true ||
|
||||
frame.Get("file").MustString() != "panicwrap_test.go" ||
|
||||
frame.Get("method").MustString() != "panick" ||
|
||||
frame.Get("lineNumber").MustInt() == 0 {
|
||||
t.Errorf("stack trace seemed wrong")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
if os.Getenv("please_panic") != "" {
|
||||
Configure(Configuration{APIKey: os.Getenv("BUGSNAG_API_KEY"), Endpoint: os.Getenv("BUGSNAG_ENDPOINT"), ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"}})
|
||||
go func() {
|
||||
panick()
|
||||
}()
|
||||
// Plenty of time to crash, it shouldn't need any of it.
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func panick() {
|
||||
panic("ruh roh")
|
||||
}
|
96
vendor/github.com/bugsnag/bugsnag-go/payload.go
generated
vendored
Normal file
96
vendor/github.com/bugsnag/bugsnag-go/payload.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
package bugsnag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type payload struct {
|
||||
*Event
|
||||
*Configuration
|
||||
}
|
||||
|
||||
type hash map[string]interface{}
|
||||
|
||||
func (p *payload) deliver() error {
|
||||
|
||||
if len(p.APIKey) != 32 {
|
||||
return fmt.Errorf("bugsnag/payload.deliver: invalid api key")
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(p)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("bugsnag/payload.deliver: %v", err)
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Transport: p.Transport,
|
||||
}
|
||||
|
||||
resp, err := client.Post(p.Endpoint, "application/json", bytes.NewBuffer(buf))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("bugsnag/payload.deliver: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("bugsnag/payload.deliver: Got HTTP %s\n", resp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *payload) MarshalJSON() ([]byte, error) {
|
||||
|
||||
data := hash{
|
||||
"apiKey": p.APIKey,
|
||||
|
||||
"notifier": hash{
|
||||
"name": "Bugsnag Go",
|
||||
"url": "https://github.com/bugsnag/bugsnag-go",
|
||||
"version": VERSION,
|
||||
},
|
||||
|
||||
"events": []hash{
|
||||
{
|
||||
"payloadVersion": "2",
|
||||
"exceptions": []hash{
|
||||
{
|
||||
"errorClass": p.ErrorClass,
|
||||
"message": p.Message,
|
||||
"stacktrace": p.Stacktrace,
|
||||
},
|
||||
},
|
||||
"severity": p.Severity.String,
|
||||
"app": hash{
|
||||
"releaseStage": p.ReleaseStage,
|
||||
},
|
||||
"user": p.User,
|
||||
"metaData": p.MetaData.sanitize(p.ParamsFilters),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
event := data["events"].([]hash)[0]
|
||||
|
||||
if p.Context != "" {
|
||||
event["context"] = p.Context
|
||||
}
|
||||
if p.GroupingHash != "" {
|
||||
event["groupingHash"] = p.GroupingHash
|
||||
}
|
||||
if p.Hostname != "" {
|
||||
event["device"] = hash{
|
||||
"hostname": p.Hostname,
|
||||
}
|
||||
}
|
||||
if p.AppVersion != "" {
|
||||
event["app"].(hash)["version"] = p.AppVersion
|
||||
}
|
||||
return json.Marshal(data)
|
||||
|
||||
}
|
60
vendor/github.com/bugsnag/bugsnag-go/revel/bugsnagrevel.go
generated
vendored
Normal file
60
vendor/github.com/bugsnag/bugsnag-go/revel/bugsnagrevel.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Package bugsnagrevel adds Bugsnag to revel.
|
||||
// It lets you pass *revel.Controller into bugsnag.Notify(),
|
||||
// and provides a Filter to catch errors.
|
||||
package bugsnagrevel
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
// Filter should be added to the filter chain just after the PanicFilter.
|
||||
// It sends errors to Bugsnag automatically. Configuration is read out of
|
||||
// conf/app.conf, you should set bugsnag.apikey, and can also set
|
||||
// bugsnag.endpoint, bugsnag.releasestage, bugsnag.appversion,
|
||||
// bugsnag.projectroot, bugsnag.projectpackages if needed.
|
||||
func Filter(c *revel.Controller, fc []revel.Filter) {
|
||||
defer bugsnag.AutoNotify(c)
|
||||
fc[0](c, fc[1:])
|
||||
}
|
||||
|
||||
// Add support to bugsnag for reading data out of *revel.Controllers
|
||||
func middleware(event *bugsnag.Event, config *bugsnag.Configuration) error {
|
||||
for _, datum := range event.RawData {
|
||||
if controller, ok := datum.(*revel.Controller); ok {
|
||||
// make the request visible to the builtin HttpMIddleware
|
||||
event.RawData = append(event.RawData, controller.Request.Request)
|
||||
event.Context = controller.Action
|
||||
event.MetaData.AddStruct("Session", controller.Session)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
bugsnag.OnBeforeNotify(middleware)
|
||||
|
||||
var projectPackages []string
|
||||
if packages, ok := revel.Config.String("bugsnag.projectpackages"); ok {
|
||||
projectPackages = strings.Split(packages, ",")
|
||||
} else {
|
||||
projectPackages = []string{revel.ImportPath + "/app/*", revel.ImportPath + "/app"}
|
||||
}
|
||||
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
APIKey: revel.Config.StringDefault("bugsnag.apikey", ""),
|
||||
Endpoint: revel.Config.StringDefault("bugsnag.endpoint", ""),
|
||||
AppVersion: revel.Config.StringDefault("bugsnag.appversion", ""),
|
||||
ReleaseStage: revel.Config.StringDefault("bugsnag.releasestage", revel.RunMode),
|
||||
ProjectPackages: projectPackages,
|
||||
Logger: revel.ERROR,
|
||||
})
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue