commit
4f7424c8eb
43 changed files with 1 additions and 2992 deletions
|
@ -62,9 +62,6 @@ type Configuration struct {
|
|||
// Middleware lists all middlewares to be used by the registry.
|
||||
Middleware map[string][]Middleware `yaml:"middleware,omitempty"`
|
||||
|
||||
// Reporting is the configuration for error reporting
|
||||
Reporting Reporting `yaml:"reporting,omitempty"`
|
||||
|
||||
// HTTP contains configuration parameters for the registry's http
|
||||
// interface.
|
||||
HTTP struct {
|
||||
|
@ -627,23 +624,6 @@ type Ignore struct {
|
|||
Actions []string `yaml:"actions"` // ignore action types
|
||||
}
|
||||
|
||||
// Reporting defines error reporting methods.
|
||||
type Reporting struct {
|
||||
// Bugsnag configures error reporting for Bugsnag (bugsnag.com).
|
||||
Bugsnag BugsnagReporting `yaml:"bugsnag,omitempty"`
|
||||
}
|
||||
|
||||
// BugsnagReporting configures error reporting for Bugsnag (bugsnag.com).
|
||||
type BugsnagReporting struct {
|
||||
// APIKey is the Bugsnag api key.
|
||||
APIKey string `yaml:"apikey,omitempty"`
|
||||
// ReleaseStage tracks where the registry is deployed.
|
||||
// Examples: production, staging, development
|
||||
ReleaseStage string `yaml:"releasestage,omitempty"`
|
||||
// Endpoint is used for specifying an enterprise Bugsnag endpoint.
|
||||
Endpoint string `yaml:"endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// Middleware configures named middlewares to be applied at injection points.
|
||||
type Middleware struct {
|
||||
// Name the middleware registers itself as
|
||||
|
|
|
@ -50,11 +50,6 @@ var configStruct = Configuration{
|
|||
"service": "silly",
|
||||
},
|
||||
},
|
||||
Reporting: Reporting{
|
||||
Bugsnag: BugsnagReporting{
|
||||
APIKey: "BugsnagApiKey",
|
||||
},
|
||||
},
|
||||
Notifications: Notifications{
|
||||
Endpoints: []Endpoint{
|
||||
{
|
||||
|
@ -201,9 +196,6 @@ notifications:
|
|||
- application/octet-stream
|
||||
actions:
|
||||
- pull
|
||||
reporting:
|
||||
bugsnag:
|
||||
apikey: BugsnagApiKey
|
||||
http:
|
||||
clientcas:
|
||||
- /path/to/ca.pem
|
||||
|
@ -286,7 +278,6 @@ func (suite *ConfigSuite) TestParseSimple(c *check.C) {
|
|||
// a string can be parsed into a Configuration struct with no storage parameters
|
||||
func (suite *ConfigSuite) TestParseInmemory(c *check.C) {
|
||||
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
||||
suite.expectedConfig.Reporting = Reporting{}
|
||||
suite.expectedConfig.Log.Fields = nil
|
||||
suite.expectedConfig.Redis = struct {
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
|
@ -322,7 +313,6 @@ func (suite *ConfigSuite) TestParseIncomplete(c *check.C) {
|
|||
suite.expectedConfig.Log.Fields = nil
|
||||
suite.expectedConfig.Storage = Storage{"filesystem": Parameters{"rootdirectory": "/tmp/testroot"}}
|
||||
suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}}
|
||||
suite.expectedConfig.Reporting = Reporting{}
|
||||
suite.expectedConfig.Notifications = Notifications{}
|
||||
suite.expectedConfig.HTTP.Headers = nil
|
||||
suite.expectedConfig.Redis = struct {
|
||||
|
@ -448,20 +438,6 @@ func (suite *ConfigSuite) TestParseInvalidLoglevel(c *check.C) {
|
|||
c.Assert(err, check.NotNil)
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvReporting validates that environment variables
|
||||
// properly override reporting parameters
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvReporting(c *check.C) {
|
||||
suite.expectedConfig.Reporting.Bugsnag.APIKey = "anotherBugsnagApiKey"
|
||||
suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
|
||||
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_APIKEY", "anotherBugsnagApiKey")
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(config, check.DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseInvalidVersion validates that the parser will fail to parse a newer configuration
|
||||
// version than the CurrentVersion
|
||||
func (suite *ConfigSuite) TestParseInvalidVersion(c *check.C) {
|
||||
|
@ -475,10 +451,6 @@ func (suite *ConfigSuite) TestParseInvalidVersion(c *check.C) {
|
|||
// TestParseExtraneousVars validates that environment variables referring to
|
||||
// nonexistent variables don't cause side effects.
|
||||
func (suite *ConfigSuite) TestParseExtraneousVars(c *check.C) {
|
||||
suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
|
||||
|
||||
// A valid environment variable
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
|
||||
|
||||
// Environment variables which shouldn't set config items
|
||||
os.Setenv("REGISTRY_DUCKS", "quack")
|
||||
|
@ -611,9 +583,6 @@ func copyConfig(config Configuration) *Configuration {
|
|||
for k, v := range config.Storage.Parameters() {
|
||||
configCopy.Storage.setParameter(k, v)
|
||||
}
|
||||
configCopy.Reporting = Reporting{
|
||||
Bugsnag: BugsnagReporting{config.Reporting.Bugsnag.APIKey, config.Reporting.Bugsnag.ReleaseStage, config.Reporting.Bugsnag.Endpoint},
|
||||
}
|
||||
|
||||
configCopy.Auth = Auth{config.Auth.Type(): Parameters{}}
|
||||
for k, v := range config.Auth.Parameters() {
|
||||
|
|
|
@ -192,11 +192,6 @@ middleware:
|
|||
- name: redirect
|
||||
options:
|
||||
baseurl: https://example.com/
|
||||
reporting:
|
||||
bugsnag:
|
||||
apikey: bugsnagapikey
|
||||
releasestage: bugsnagreleasestage
|
||||
endpoint: bugsnagendpoint
|
||||
http:
|
||||
addr: localhost:5000
|
||||
prefix: /my/nested/registry/
|
||||
|
@ -699,31 +694,6 @@ location of a proxy for the layer stored by the S3 storage driver.
|
|||
|-----------|----------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `baseurl` | yes | `SCHEME://HOST` at which layers are served. Can also contain port. For example, `https://example.com:5443`. |
|
||||
|
||||
## `reporting`
|
||||
|
||||
```
|
||||
reporting:
|
||||
bugsnag:
|
||||
apikey: bugsnagapikey
|
||||
releasestage: bugsnagreleasestage
|
||||
endpoint: bugsnagendpoint
|
||||
```
|
||||
|
||||
The `reporting` option is **optional** and configures error and metrics
|
||||
reporting tools. At the moment only two services are supported:
|
||||
|
||||
- [Bugsnag](#bugsnag)
|
||||
|
||||
A valid configuration may contain both.
|
||||
|
||||
### `bugsnag`
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------------------------------------------------|
|
||||
| `apikey` | yes | The API Key provided by Bugsnag. |
|
||||
| `releasestage` | no | Tracks where the registry is deployed, using a string like `production`, `staging`, or `development`.|
|
||||
| `endpoint`| no | The enterprise Bugsnag endpoint. |
|
||||
|
||||
## `http`
|
||||
|
||||
```none
|
||||
|
|
6
go.mod
6
go.mod
|
@ -8,10 +8,8 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d
|
||||
github.com/aws/aws-sdk-go v1.44.325
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
|
||||
github.com/docker/go-metrics v0.0.1
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
|
||||
|
@ -40,9 +38,6 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||
|
@ -60,7 +55,6 @@ require (
|
|||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect; updated to latest
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -56,8 +56,6 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkM
|
|||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
@ -69,16 +67,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
@ -235,8 +225,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
|
|
|
@ -12,10 +12,7 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
logrus_bugsnag "github.com/Shopify/logrus-bugsnag"
|
||||
|
||||
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/docker/go-metrics"
|
||||
gorhandlers "github.com/gorilla/handlers"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -139,8 +136,6 @@ func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Reg
|
|||
return nil, fmt.Errorf("error configuring logger: %v", err)
|
||||
}
|
||||
|
||||
configureBugsnag(config)
|
||||
|
||||
// inject a logger into the uuid library. warns us if there is a problem
|
||||
// with uuid generation under low entropy.
|
||||
uuid.Loggerf = dcontext.GetLogger(ctx).Warnf
|
||||
|
@ -149,7 +144,7 @@ func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Reg
|
|||
// TODO(aaronl): The global scope of the health checks means NewRegistry
|
||||
// can only be called once per process.
|
||||
app.RegisterHealthChecks()
|
||||
handler := configureReporting(app)
|
||||
var handler http.Handler = app
|
||||
handler = alive("/", handler)
|
||||
handler = health.Handler(handler)
|
||||
handler = panicHandler(handler)
|
||||
|
@ -345,16 +340,6 @@ func configurePrometheus(config *configuration.Configuration) {
|
|||
}
|
||||
}
|
||||
|
||||
func configureReporting(app *handlers.App) http.Handler {
|
||||
var handler http.Handler = app
|
||||
|
||||
if app.Config.Reporting.Bugsnag.APIKey != "" {
|
||||
handler = bugsnag.Handler(handler)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// configureLogging prepares the context with a logger using the
|
||||
// configuration.
|
||||
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
|
||||
|
@ -410,32 +395,6 @@ func logLevel(level configuration.Loglevel) logrus.Level {
|
|||
return l
|
||||
}
|
||||
|
||||
// configureBugsnag configures bugsnag reporting, if enabled
|
||||
func configureBugsnag(config *configuration.Configuration) {
|
||||
if config.Reporting.Bugsnag.APIKey == "" {
|
||||
return
|
||||
}
|
||||
|
||||
bugsnagConfig := bugsnag.Configuration{
|
||||
APIKey: config.Reporting.Bugsnag.APIKey,
|
||||
}
|
||||
if config.Reporting.Bugsnag.ReleaseStage != "" {
|
||||
bugsnagConfig.ReleaseStage = config.Reporting.Bugsnag.ReleaseStage
|
||||
}
|
||||
if config.Reporting.Bugsnag.Endpoint != "" {
|
||||
bugsnagConfig.Endpoint = config.Reporting.Bugsnag.Endpoint
|
||||
}
|
||||
bugsnag.Configure(bugsnagConfig)
|
||||
|
||||
// configure logrus bugsnag hook
|
||||
hook, err := logrus_bugsnag.NewBugsnagHook()
|
||||
if err != nil {
|
||||
logrus.Fatalln(err)
|
||||
}
|
||||
|
||||
logrus.AddHook(hook)
|
||||
}
|
||||
|
||||
// panicHandler add an HTTP handler to web app. The handler recover the happening
|
||||
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
|
||||
// defined in config.yml.
|
||||
|
|
4
vendor/github.com/Shopify/logrus-bugsnag/.travis.yml
generated
vendored
4
vendor/github.com/Shopify/logrus-bugsnag/.travis.yml
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
21
vendor/github.com/Shopify/logrus-bugsnag/LICENSE
generated
vendored
21
vendor/github.com/Shopify/logrus-bugsnag/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Shopify
|
||||
|
||||
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.
|
24
vendor/github.com/Shopify/logrus-bugsnag/README.md
generated
vendored
24
vendor/github.com/Shopify/logrus-bugsnag/README.md
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
## logrus-bugsnag
|
||||
|
||||
[![Build Status](https://travis-ci.org/Shopify/logrus-bugsnag.svg)](https://travis-ci.org/Shopify/logrus-bugsnag)
|
||||
|
||||
logrus-bugsnag is a hook that allows [Logrus](https://github.com/sirupsen/logrus) to interface with [Bugsnag](https://bugsnag.com).
|
||||
|
||||
#### Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/Shopify/logrus-bugsnag"
|
||||
bugsnag "github.com/bugsnag/bugsnag-go"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
APIKey: apiKey,
|
||||
})
|
||||
hook, err := logrus_bugsnag.NewBugsnagHook()
|
||||
logrus.StandardLogger().Hooks.Add(hook)
|
||||
}
|
||||
```
|
||||
|
81
vendor/github.com/Shopify/logrus-bugsnag/bugsnag.go
generated
vendored
81
vendor/github.com/Shopify/logrus-bugsnag/bugsnag.go
generated
vendored
|
@ -1,81 +0,0 @@
|
|||
package logrus_bugsnag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
bugsnag_errors "github.com/bugsnag/bugsnag-go/errors"
|
||||
)
|
||||
|
||||
type bugsnagHook struct{}
|
||||
|
||||
// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
|
||||
// bugsnag.Configure. Bugsnag must be configured before the hook.
|
||||
var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
|
||||
|
||||
// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
|
||||
// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
|
||||
// failed.
|
||||
type ErrBugsnagSendFailed struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e ErrBugsnagSendFailed) Error() string {
|
||||
return "failed to send error to Bugsnag: " + e.err.Error()
|
||||
}
|
||||
|
||||
// NewBugsnagHook initializes a logrus hook which sends exceptions to an
|
||||
// exception-tracking service compatible with the Bugsnag API. Before using
|
||||
// this hook, you must call bugsnag.Configure(). The returned object should be
|
||||
// registered with a log via `AddHook()`
|
||||
//
|
||||
// Entries that trigger an Error, Fatal or Panic should now include an "error"
|
||||
// field to send to Bugsnag.
|
||||
func NewBugsnagHook() (*bugsnagHook, error) {
|
||||
if bugsnag.Config.APIKey == "" {
|
||||
return nil, ErrBugsnagUnconfigured
|
||||
}
|
||||
return &bugsnagHook{}, nil
|
||||
}
|
||||
|
||||
// skipStackFrames skips logrus stack frames before logging to Bugsnag.
|
||||
const skipStackFrames = 4
|
||||
|
||||
// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
|
||||
// "error" field (or the Message if the error isn't present) and sends it off.
|
||||
func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
|
||||
var notifyErr error
|
||||
err, ok := entry.Data["error"].(error)
|
||||
if ok {
|
||||
notifyErr = err
|
||||
} else {
|
||||
notifyErr = errors.New(entry.Message)
|
||||
}
|
||||
|
||||
metadata := bugsnag.MetaData{}
|
||||
metadata["metadata"] = make(map[string]interface{})
|
||||
for key, val := range entry.Data {
|
||||
if key != "error" {
|
||||
metadata["metadata"][key] = val
|
||||
}
|
||||
}
|
||||
|
||||
errWithStack := bugsnag_errors.New(notifyErr, skipStackFrames)
|
||||
bugsnagErr := bugsnag.Notify(errWithStack, metadata)
|
||||
if bugsnagErr != nil {
|
||||
return ErrBugsnagSendFailed{bugsnagErr}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels enumerates the log levels on which the error should be forwarded to
|
||||
// bugsnag: everything at or above the "Error" level.
|
||||
func (hook *bugsnagHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}
|
||||
}
|
8
vendor/github.com/Shopify/logrus-bugsnag/dev.yml
generated
vendored
8
vendor/github.com/Shopify/logrus-bugsnag/dev.yml
generated
vendored
|
@ -1,8 +0,0 @@
|
|||
name: logrus-bugsnag
|
||||
|
||||
up:
|
||||
- go: 1.6.2
|
||||
|
||||
commands:
|
||||
test:
|
||||
run: go get -t ./... && go test ./...
|
13
vendor/github.com/bugsnag/bugsnag-go/.travis.yml
generated
vendored
13
vendor/github.com/bugsnag/bugsnag-go/.travis.yml
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
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
20
vendor/github.com/bugsnag/bugsnag-go/LICENSE.txt
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
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
489
vendor/github.com/bugsnag/bugsnag-go/README.md
generated
vendored
|
@ -1,489 +0,0 @@
|
|||
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
76
vendor/github.com/bugsnag/bugsnag-go/appengine.go
generated
vendored
|
@ -1,76 +0,0 @@
|
|||
// +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
131
vendor/github.com/bugsnag/bugsnag-go/bugsnag.go
generated
vendored
|
@ -1,131 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
159
vendor/github.com/bugsnag/bugsnag-go/configuration.go
generated
vendored
159
vendor/github.com/bugsnag/bugsnag-go/configuration.go
generated
vendored
|
@ -1,159 +0,0 @@
|
|||
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
|
||||
}
|
69
vendor/github.com/bugsnag/bugsnag-go/doc.go
generated
vendored
69
vendor/github.com/bugsnag/bugsnag-go/doc.go
generated
vendored
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
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
6
vendor/github.com/bugsnag/bugsnag-go/errors/README.md
generated
vendored
|
@ -1,6 +0,0 @@
|
|||
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
90
vendor/github.com/bugsnag/bugsnag-go/errors/error.go
generated
vendored
|
@ -1,90 +0,0 @@
|
|||
// 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()
|
||||
}
|
127
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic.go
generated
vendored
127
vendor/github.com/bugsnag/bugsnag-go/errors/parse_panic.go
generated
vendored
|
@ -1,127 +0,0 @@
|
|||
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
|
||||
}
|
97
vendor/github.com/bugsnag/bugsnag-go/errors/stackframe.go
generated
vendored
97
vendor/github.com/bugsnag/bugsnag-go/errors/stackframe.go
generated
vendored
|
@ -1,97 +0,0 @@
|
|||
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
134
vendor/github.com/bugsnag/bugsnag-go/event.go
generated
vendored
|
@ -1,134 +0,0 @@
|
|||
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
43
vendor/github.com/bugsnag/bugsnag-go/json_tags.go
generated
vendored
|
@ -1,43 +0,0 @@
|
|||
// 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
185
vendor/github.com/bugsnag/bugsnag-go/metadata.go
generated
vendored
|
@ -1,185 +0,0 @@
|
|||
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
|
||||
}
|
96
vendor/github.com/bugsnag/bugsnag-go/middleware.go
generated
vendored
96
vendor/github.com/bugsnag/bugsnag-go/middleware.go
generated
vendored
|
@ -1,96 +0,0 @@
|
|||
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
|
||||
}
|
95
vendor/github.com/bugsnag/bugsnag-go/notifier.go
generated
vendored
95
vendor/github.com/bugsnag/bugsnag-go/notifier.go
generated
vendored
|
@ -1,95 +0,0 @@
|
|||
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
27
vendor/github.com/bugsnag/bugsnag-go/panicwrap.go
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
// +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)
|
||||
}
|
||||
}
|
96
vendor/github.com/bugsnag/bugsnag-go/payload.go
generated
vendored
96
vendor/github.com/bugsnag/bugsnag-go/payload.go
generated
vendored
|
@ -1,96 +0,0 @@
|
|||
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)
|
||||
|
||||
}
|
20
vendor/github.com/bugsnag/osext/LICENSE
generated
vendored
20
vendor/github.com/bugsnag/osext/LICENSE
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2012 Daniel Theophanes
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
32
vendor/github.com/bugsnag/osext/osext.go
generated
vendored
32
vendor/github.com/bugsnag/osext/osext.go
generated
vendored
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Extensions to the standard "os" package.
|
||||
package osext
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// Executable returns an absolute path that can be used to
|
||||
// re-invoke the current program.
|
||||
// It may not be valid after the current program exits.
|
||||
func Executable() (string, error) {
|
||||
p, err := executable()
|
||||
return filepath.Clean(p), err
|
||||
}
|
||||
|
||||
// Returns same path as Executable, returns just the folder
|
||||
// path. Excludes the executable name.
|
||||
func ExecutableFolder() (string, error) {
|
||||
p, err := Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
folder, _ := filepath.Split(p)
|
||||
return folder, nil
|
||||
}
|
||||
|
||||
// Depricated. Same as Executable().
|
||||
func GetExePath() (exePath string, err error) {
|
||||
return Executable()
|
||||
}
|
16
vendor/github.com/bugsnag/osext/osext_plan9.go
generated
vendored
16
vendor/github.com/bugsnag/osext/osext_plan9.go
generated
vendored
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import "syscall"
|
||||
|
||||
func executable() (string, error) {
|
||||
f, err := Open("/proc/" + itoa(Getpid()) + "/text")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
return syscall.Fd2path(int(f.Fd()))
|
||||
}
|
25
vendor/github.com/bugsnag/osext/osext_procfs.go
generated
vendored
25
vendor/github.com/bugsnag/osext/osext_procfs.go
generated
vendored
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux netbsd openbsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func executable() (string, error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return os.Readlink("/proc/self/exe")
|
||||
case "netbsd":
|
||||
return os.Readlink("/proc/curproc/exe")
|
||||
case "openbsd":
|
||||
return os.Readlink("/proc/curproc/file")
|
||||
}
|
||||
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||
}
|
64
vendor/github.com/bugsnag/osext/osext_sysctl.go
generated
vendored
64
vendor/github.com/bugsnag/osext/osext_sysctl.go
generated
vendored
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin freebsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var startUpcwd, getwdError = os.Getwd()
|
||||
|
||||
func executable() (string, error) {
|
||||
var mib [4]int32
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||
case "darwin":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||
}
|
||||
|
||||
n := uintptr(0)
|
||||
// get length
|
||||
_, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
if n == 0 { // shouldn't happen
|
||||
return "", nil
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
_, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
if n == 0 { // shouldn't happen
|
||||
return "", nil
|
||||
}
|
||||
for i, v := range buf {
|
||||
if v == 0 {
|
||||
buf = buf[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if buf[0] != '/' {
|
||||
if getwdError != nil {
|
||||
return string(buf), getwdError
|
||||
} else {
|
||||
if buf[0] == '.' {
|
||||
buf = buf[1:]
|
||||
}
|
||||
if startUpcwd[len(startUpcwd)-1] != '/' {
|
||||
return startUpcwd + "/" + string(buf), nil
|
||||
}
|
||||
return startUpcwd + string(buf), nil
|
||||
}
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
34
vendor/github.com/bugsnag/osext/osext_windows.go
generated
vendored
34
vendor/github.com/bugsnag/osext/osext_windows.go
generated
vendored
|
@ -1,34 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||
)
|
||||
|
||||
// GetModuleFileName() with hModule = NULL
|
||||
func executable() (exePath string, err error) {
|
||||
return getModuleFileName()
|
||||
}
|
||||
|
||||
func getModuleFileName() (string, error) {
|
||||
var n uint32
|
||||
b := make([]uint16, syscall.MAX_PATH)
|
||||
size := uint32(len(b))
|
||||
|
||||
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||
n = uint32(r0)
|
||||
if n == 0 {
|
||||
return "", e1
|
||||
}
|
||||
return string(utf16.Decode(b[0:n])), nil
|
||||
}
|
21
vendor/github.com/bugsnag/panicwrap/LICENSE
generated
vendored
21
vendor/github.com/bugsnag/panicwrap/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Mitchell Hashimoto
|
||||
|
||||
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.
|
101
vendor/github.com/bugsnag/panicwrap/README.md
generated
vendored
101
vendor/github.com/bugsnag/panicwrap/README.md
generated
vendored
|
@ -1,101 +0,0 @@
|
|||
# panicwrap
|
||||
|
||||
panicwrap is a Go library that re-executes a Go binary and monitors stderr
|
||||
output from the binary for a panic. When it find a panic, it executes a
|
||||
user-defined handler function. Stdout, stderr, stdin, signals, and exit
|
||||
codes continue to work as normal, making the existence of panicwrap mostly
|
||||
invisble to the end user until a panic actually occurs.
|
||||
|
||||
Since a panic is truly a bug in the program meant to crash the runtime,
|
||||
globally catching panics within Go applications is not supposed to be possible.
|
||||
Despite this, it is often useful to have a way to know when panics occur.
|
||||
panicwrap allows you to do something with these panics, such as writing them
|
||||
to a file, so that you can track when panics occur.
|
||||
|
||||
panicwrap is ***not a panic recovery system***. Panics indicate serious
|
||||
problems with your application and _should_ crash the runtime. panicwrap
|
||||
is just meant as a way to monitor for panics. If you still think this is
|
||||
the worst idea ever, read the section below on why.
|
||||
|
||||
## Features
|
||||
|
||||
* **SIMPLE!**
|
||||
* Works with all Go applications on all platforms Go supports
|
||||
* Custom behavior when a panic occurs
|
||||
* Stdout, stderr, stdin, exit codes, and signals continue to work as
|
||||
expected.
|
||||
|
||||
## Usage
|
||||
|
||||
Using panicwrap is simple. It behaves a lot like `fork`, if you know
|
||||
how that works. A basic example is shown below.
|
||||
|
||||
Because it would be sad to panic while capturing a panic, it is recommended
|
||||
that the handler functions for panicwrap remain relatively simple and well
|
||||
tested. panicwrap itself contains many tests.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/panicwrap"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
exitStatus, err := panicwrap.BasicWrap(panicHandler)
|
||||
if err != nil {
|
||||
// Something went wrong setting up the panic wrapper. Unlikely,
|
||||
// but possible.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// If exitStatus >= 0, then we're the parent process and the panicwrap
|
||||
// re-executed ourselves and completed. Just exit with the proper status.
|
||||
if exitStatus >= 0 {
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
|
||||
// Otherwise, exitStatus < 0 means we're the child. Continue executing as
|
||||
// normal...
|
||||
|
||||
// Let's say we panic
|
||||
panic("oh shucks")
|
||||
}
|
||||
|
||||
func panicHandler(output string) {
|
||||
// output contains the full output (including stack traces) of the
|
||||
// panic. Put it in a file or something.
|
||||
fmt.Printf("The child panicked:\n\n%s\n", output)
|
||||
os.Exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
## How Does it Work?
|
||||
|
||||
panicwrap works by re-executing the running program (retaining arguments,
|
||||
environmental variables, etc.) and monitoring the stderr of the program.
|
||||
Since Go always outputs panics in a predictable way with a predictable
|
||||
exit code, panicwrap is able to reliably detect panics and allow the parent
|
||||
process to handle them.
|
||||
|
||||
## WHY?! Panics should CRASH!
|
||||
|
||||
Yes, panics _should_ crash. They are 100% always indicative of bugs.
|
||||
However, in some cases, such as user-facing programs (programs like
|
||||
[Packer](http://github.com/mitchellh/packer) or
|
||||
[Docker](http://github.com/dotcloud/docker)), it is up to the user to
|
||||
report such panics. This is unreliable, at best, and it would be better if the
|
||||
program could have a way to automatically report panics. panicwrap provides
|
||||
a way to do this.
|
||||
|
||||
For backend applications, it is easier to detect crashes (since the application
|
||||
exits). However, it is still nice sometimes to more intelligently log
|
||||
panics in some way. For example, at [HashiCorp](http://www.hashicorp.com),
|
||||
we use panicwrap to log panics to timestamped files with some additional
|
||||
data (configuration settings at the time, environmental variables, etc.)
|
||||
|
||||
The goal of panicwrap is _not_ to hide panics. It is instead to provide
|
||||
a clean mechanism for handling them before bubbling the up to the user
|
||||
and ultimately crashing.
|
11
vendor/github.com/bugsnag/panicwrap/dup2.go
generated
vendored
11
vendor/github.com/bugsnag/panicwrap/dup2.go
generated
vendored
|
@ -1,11 +0,0 @@
|
|||
// +build darwin dragonfly freebsd linux,!arm64 netbsd openbsd
|
||||
|
||||
package panicwrap
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func dup2(oldfd, newfd int) error {
|
||||
return syscall.Dup2(oldfd, newfd)
|
||||
}
|
11
vendor/github.com/bugsnag/panicwrap/dup3.go
generated
vendored
11
vendor/github.com/bugsnag/panicwrap/dup3.go
generated
vendored
|
@ -1,11 +0,0 @@
|
|||
// +build linux,arm64
|
||||
|
||||
package panicwrap
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func dup2(oldfd, newfd int) error {
|
||||
return syscall.Dup3(oldfd, newfd, 0)
|
||||
}
|
62
vendor/github.com/bugsnag/panicwrap/monitor.go
generated
vendored
62
vendor/github.com/bugsnag/panicwrap/monitor.go
generated
vendored
|
@ -1,62 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package panicwrap
|
||||
|
||||
import (
|
||||
"github.com/bugsnag/osext"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func monitor(c *WrapConfig) (int, error) {
|
||||
|
||||
// If we're the child process, absorb panics.
|
||||
if Wrapped(c) {
|
||||
panicCh := make(chan string)
|
||||
|
||||
go trackPanic(os.Stdin, os.Stderr, c.DetectDuration, panicCh)
|
||||
|
||||
// Wait on the panic data
|
||||
panicTxt := <-panicCh
|
||||
if panicTxt != "" {
|
||||
if !c.HidePanic {
|
||||
os.Stderr.Write([]byte(panicTxt))
|
||||
}
|
||||
|
||||
c.Handler(panicTxt)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
exePath, err := osext.Executable()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
cmd := exec.Command(exePath, os.Args[1:]...)
|
||||
|
||||
read, write, err := os.Pipe()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
cmd.Stdin = read
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = append(os.Environ(), c.CookieKey+"="+c.CookieValue)
|
||||
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
err = dup2(int(write.Fd()), int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
7
vendor/github.com/bugsnag/panicwrap/monitor_windows.go
generated
vendored
7
vendor/github.com/bugsnag/panicwrap/monitor_windows.go
generated
vendored
|
@ -1,7 +0,0 @@
|
|||
package panicwrap
|
||||
|
||||
import "fmt"
|
||||
|
||||
func monitor(c *WrapConfig) (int, error) {
|
||||
return -1, fmt.Errorf("Monitor is not supported on windows")
|
||||
}
|
339
vendor/github.com/bugsnag/panicwrap/panicwrap.go
generated
vendored
339
vendor/github.com/bugsnag/panicwrap/panicwrap.go
generated
vendored
|
@ -1,339 +0,0 @@
|
|||
// The panicwrap package provides functions for capturing and handling
|
||||
// panics in your application. It does this by re-executing the running
|
||||
// application and monitoring stderr for any panics. At the same time,
|
||||
// stdout/stderr/etc. are set to the same values so that data is shuttled
|
||||
// through properly, making the existence of panicwrap mostly transparent.
|
||||
//
|
||||
// Panics are only detected when the subprocess exits with a non-zero
|
||||
// exit status, since this is the only time panics are real. Otherwise,
|
||||
// "panic-like" output is ignored.
|
||||
package panicwrap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/bugsnag/osext"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_COOKIE_KEY = "cccf35992f8f3cd8d1d28f0109dd953e26664531"
|
||||
DEFAULT_COOKIE_VAL = "7c28215aca87789f95b406b8dd91aa5198406750"
|
||||
)
|
||||
|
||||
// HandlerFunc is the type called when a panic is detected.
|
||||
type HandlerFunc func(string)
|
||||
|
||||
// WrapConfig is the configuration for panicwrap when wrapping an existing
|
||||
// binary. To get started, in general, you only need the BasicWrap function
|
||||
// that will set this up for you. However, for more customizability,
|
||||
// WrapConfig and Wrap can be used.
|
||||
type WrapConfig struct {
|
||||
// Handler is the function called when a panic occurs.
|
||||
Handler HandlerFunc
|
||||
|
||||
// The cookie key and value are used within environmental variables
|
||||
// to tell the child process that it is already executing so that
|
||||
// wrap doesn't re-wrap itself.
|
||||
CookieKey string
|
||||
CookieValue string
|
||||
|
||||
// If true, the panic will not be mirrored to the configured writer
|
||||
// and will instead ONLY go to the handler. This lets you effectively
|
||||
// hide panics from the end user. This is not recommended because if
|
||||
// your handler fails, the panic is effectively lost.
|
||||
HidePanic bool
|
||||
|
||||
// If true, panicwrap will boot a monitor sub-process and let the parent
|
||||
// run the app. This mode is useful for processes run under supervisors
|
||||
// like runit as signals get sent to the correct codebase. This is not
|
||||
// supported when GOOS=windows, and ignores c.Stderr and c.Stdout.
|
||||
Monitor bool
|
||||
|
||||
// The amount of time that a process must exit within after detecting
|
||||
// a panic header for panicwrap to assume it is a panic. Defaults to
|
||||
// 300 milliseconds.
|
||||
DetectDuration time.Duration
|
||||
|
||||
// The writer to send the stderr to. If this is nil, then it defaults
|
||||
// to os.Stderr.
|
||||
Writer io.Writer
|
||||
|
||||
// The writer to send stdout to. If this is nil, then it defaults to
|
||||
// os.Stdout.
|
||||
Stdout io.Writer
|
||||
}
|
||||
|
||||
// BasicWrap calls Wrap with the given handler function, using defaults
|
||||
// for everything else. See Wrap and WrapConfig for more information on
|
||||
// functionality and return values.
|
||||
func BasicWrap(f HandlerFunc) (int, error) {
|
||||
return Wrap(&WrapConfig{
|
||||
Handler: f,
|
||||
})
|
||||
}
|
||||
|
||||
// BasicMonitor calls Wrap with Monitor set to true on supported platforms.
|
||||
// It forks your program and runs it again form the start. In one process
|
||||
// BasicMonitor never returns, it just listens on stderr of the other process,
|
||||
// and calls your handler when a panic is seen. In the other it either returns
|
||||
// nil to indicate that the panic monitoring is enabled, or an error to indicate
|
||||
// that something else went wrong.
|
||||
func BasicMonitor(f HandlerFunc) error {
|
||||
exitStatus, err := Wrap(&WrapConfig{
|
||||
Handler: f,
|
||||
Monitor: runtime.GOOS != "windows",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exitStatus >= 0 {
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wrap wraps the current executable in a handler to catch panics. It
|
||||
// returns an error if there was an error during the wrapping process.
|
||||
// If the error is nil, then the int result indicates the exit status of the
|
||||
// child process. If the exit status is -1, then this is the child process,
|
||||
// and execution should continue as normal. Otherwise, this is the parent
|
||||
// process and the child successfully ran already, and you should exit the
|
||||
// process with the returned exit status.
|
||||
//
|
||||
// This function should be called very very early in your program's execution.
|
||||
// Ideally, this runs as the first line of code of main.
|
||||
//
|
||||
// Once this is called, the given WrapConfig shouldn't be modified or used
|
||||
// any further.
|
||||
func Wrap(c *WrapConfig) (int, error) {
|
||||
if c.Handler == nil {
|
||||
return -1, errors.New("Handler must be set")
|
||||
}
|
||||
|
||||
if c.DetectDuration == 0 {
|
||||
c.DetectDuration = 300 * time.Millisecond
|
||||
}
|
||||
|
||||
if c.Writer == nil {
|
||||
c.Writer = os.Stderr
|
||||
}
|
||||
|
||||
if c.Monitor {
|
||||
return monitor(c)
|
||||
} else {
|
||||
return wrap(c)
|
||||
}
|
||||
}
|
||||
|
||||
func wrap(c *WrapConfig) (int, error) {
|
||||
|
||||
// If we're already wrapped, exit out.
|
||||
if Wrapped(c) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// Get the path to our current executable
|
||||
exePath, err := osext.Executable()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
// Pipe the stderr so we can read all the data as we look for panics
|
||||
stderr_r, stderr_w := io.Pipe()
|
||||
|
||||
// doneCh is closed when we're done, signaling any other goroutines
|
||||
// to end immediately.
|
||||
doneCh := make(chan struct{})
|
||||
|
||||
// panicCh is the channel on which the panic text will actually be
|
||||
// sent.
|
||||
panicCh := make(chan string)
|
||||
|
||||
// On close, make sure to finish off the copying of data to stderr
|
||||
defer func() {
|
||||
defer close(doneCh)
|
||||
stderr_w.Close()
|
||||
<-panicCh
|
||||
}()
|
||||
|
||||
// Start the goroutine that will watch stderr for any panics
|
||||
go trackPanic(stderr_r, c.Writer, c.DetectDuration, panicCh)
|
||||
|
||||
// Create the writer for stdout that we're going to use
|
||||
var stdout_w io.Writer = os.Stdout
|
||||
if c.Stdout != nil {
|
||||
stdout_w = c.Stdout
|
||||
}
|
||||
|
||||
// Build a subcommand to re-execute ourselves. We make sure to
|
||||
// set the environmental variable to include our cookie. We also
|
||||
// set stdin/stdout to match the config. Finally, we pipe stderr
|
||||
// through ourselves in order to watch for panics.
|
||||
cmd := exec.Command(exePath, os.Args[1:]...)
|
||||
cmd.Env = append(os.Environ(), c.CookieKey+"="+c.CookieValue)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = stdout_w
|
||||
cmd.Stderr = stderr_w
|
||||
if err := cmd.Start(); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
// Listen to signals and capture them forever. We allow the child
|
||||
// process to handle them in some way.
|
||||
sigCh := make(chan os.Signal)
|
||||
signal.Notify(sigCh, os.Interrupt)
|
||||
go func() {
|
||||
defer signal.Stop(sigCh)
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
return
|
||||
case <-sigCh:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
exitErr, ok := err.(*exec.ExitError)
|
||||
if !ok {
|
||||
// This is some other kind of subprocessing error.
|
||||
return 1, err
|
||||
}
|
||||
|
||||
exitStatus := 1
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
}
|
||||
|
||||
// Close the writer end so that the tracker goroutine ends at some point
|
||||
stderr_w.Close()
|
||||
|
||||
// Wait on the panic data
|
||||
panicTxt := <-panicCh
|
||||
if panicTxt != "" {
|
||||
if !c.HidePanic {
|
||||
c.Writer.Write([]byte(panicTxt))
|
||||
}
|
||||
|
||||
c.Handler(panicTxt)
|
||||
}
|
||||
|
||||
return exitStatus, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Wrapped checks if we're already wrapped according to the configuration
|
||||
// given.
|
||||
//
|
||||
// Wrapped is very cheap and can be used early to short-circuit some pre-wrap
|
||||
// logic your application may have.
|
||||
func Wrapped(c *WrapConfig) bool {
|
||||
if c.CookieKey == "" {
|
||||
c.CookieKey = DEFAULT_COOKIE_KEY
|
||||
}
|
||||
|
||||
if c.CookieValue == "" {
|
||||
c.CookieValue = DEFAULT_COOKIE_VAL
|
||||
}
|
||||
|
||||
// If the cookie key/value match our environment, then we are the
|
||||
// child, so just exit now and tell the caller that we're the child
|
||||
return os.Getenv(c.CookieKey) == c.CookieValue
|
||||
}
|
||||
|
||||
// trackPanic monitors the given reader for a panic. If a panic is detected,
|
||||
// it is outputted on the result channel. This will close the channel once
|
||||
// it is complete.
|
||||
func trackPanic(r io.Reader, w io.Writer, dur time.Duration, result chan<- string) {
|
||||
defer close(result)
|
||||
|
||||
var panicTimer <-chan time.Time
|
||||
panicBuf := new(bytes.Buffer)
|
||||
panicHeader := []byte("panic:")
|
||||
|
||||
tempBuf := make([]byte, 2048)
|
||||
for {
|
||||
var buf []byte
|
||||
var n int
|
||||
|
||||
if panicTimer == nil && panicBuf.Len() > 0 {
|
||||
// We're not tracking a panic but the buffer length is
|
||||
// greater than 0. We need to clear out that buffer, but
|
||||
// look for another panic along the way.
|
||||
|
||||
// First, remove the previous panic header so we don't loop
|
||||
w.Write(panicBuf.Next(len(panicHeader)))
|
||||
|
||||
// Next, assume that this is our new buffer to inspect
|
||||
n = panicBuf.Len()
|
||||
buf = make([]byte, n)
|
||||
copy(buf, panicBuf.Bytes())
|
||||
panicBuf.Reset()
|
||||
} else {
|
||||
var err error
|
||||
buf = tempBuf
|
||||
n, err = r.Read(buf)
|
||||
if n <= 0 && err == io.EOF {
|
||||
if panicBuf.Len() > 0 {
|
||||
// We were tracking a panic, assume it was a panic
|
||||
// and return that as the result.
|
||||
result <- panicBuf.String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if panicTimer != nil {
|
||||
// We're tracking what we think is a panic right now.
|
||||
// If the timer ended, then it is not a panic.
|
||||
isPanic := true
|
||||
select {
|
||||
case <-panicTimer:
|
||||
isPanic = false
|
||||
default:
|
||||
}
|
||||
|
||||
// No matter what, buffer the text some more.
|
||||
panicBuf.Write(buf[0:n])
|
||||
|
||||
if !isPanic {
|
||||
// It isn't a panic, stop tracking. Clean-up will happen
|
||||
// on the next iteration.
|
||||
panicTimer = nil
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
flushIdx := n
|
||||
idx := bytes.Index(buf[0:n], panicHeader)
|
||||
if idx >= 0 {
|
||||
flushIdx = idx
|
||||
}
|
||||
|
||||
// Flush to stderr what isn't a panic
|
||||
w.Write(buf[0:flushIdx])
|
||||
|
||||
if idx < 0 {
|
||||
// Not a panic so just continue along
|
||||
continue
|
||||
}
|
||||
|
||||
// We have a panic header. Write we assume is a panic os far.
|
||||
panicBuf.Write(buf[idx:n])
|
||||
panicTimer = time.After(dur)
|
||||
}
|
||||
}
|
17
vendor/modules.txt
vendored
17
vendor/modules.txt
vendored
|
@ -91,9 +91,6 @@ github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/options
|
|||
github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/shared
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/version
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go/apps/public
|
||||
# github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d
|
||||
## explicit
|
||||
github.com/Shopify/logrus-bugsnag
|
||||
# github.com/aws/aws-sdk-go v1.44.325
|
||||
## explicit; go 1.11
|
||||
github.com/aws/aws-sdk-go/aws
|
||||
|
@ -151,21 +148,9 @@ github.com/aws/aws-sdk-go/service/sts/stsiface
|
|||
# github.com/beorn7/perks v1.0.1
|
||||
## explicit; go 1.11
|
||||
github.com/beorn7/perks/quantile
|
||||
# github.com/bitly/go-simplejson v0.5.0
|
||||
## explicit
|
||||
# github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
||||
## explicit
|
||||
github.com/bshuster-repo/logrus-logstash-hook
|
||||
# github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
|
||||
## explicit
|
||||
github.com/bugsnag/bugsnag-go
|
||||
github.com/bugsnag/bugsnag-go/errors
|
||||
# github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
|
||||
## explicit
|
||||
github.com/bugsnag/osext
|
||||
# github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0
|
||||
## explicit
|
||||
github.com/bugsnag/panicwrap
|
||||
# github.com/cespare/xxhash/v2 v2.2.0
|
||||
## explicit; go 1.11
|
||||
github.com/cespare/xxhash/v2
|
||||
|
@ -265,8 +250,6 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
|
|||
# github.com/mitchellh/mapstructure v1.1.2
|
||||
## explicit
|
||||
github.com/mitchellh/mapstructure
|
||||
# github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f
|
||||
## explicit
|
||||
# github.com/opencontainers/go-digest v1.0.0
|
||||
## explicit; go 1.13
|
||||
github.com/opencontainers/go-digest
|
||||
|
|
Loading…
Reference in a new issue