forked from TrueCloudLab/distribution
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 lists all middlewares to be used by the registry.
|
||||||
Middleware map[string][]Middleware `yaml:"middleware,omitempty"`
|
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
|
// HTTP contains configuration parameters for the registry's http
|
||||||
// interface.
|
// interface.
|
||||||
HTTP struct {
|
HTTP struct {
|
||||||
|
@ -627,23 +624,6 @@ type Ignore struct {
|
||||||
Actions []string `yaml:"actions"` // ignore action types
|
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.
|
// Middleware configures named middlewares to be applied at injection points.
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
// Name the middleware registers itself as
|
// Name the middleware registers itself as
|
||||||
|
|
|
@ -50,11 +50,6 @@ var configStruct = Configuration{
|
||||||
"service": "silly",
|
"service": "silly",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Reporting: Reporting{
|
|
||||||
Bugsnag: BugsnagReporting{
|
|
||||||
APIKey: "BugsnagApiKey",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Notifications: Notifications{
|
Notifications: Notifications{
|
||||||
Endpoints: []Endpoint{
|
Endpoints: []Endpoint{
|
||||||
{
|
{
|
||||||
|
@ -201,9 +196,6 @@ notifications:
|
||||||
- application/octet-stream
|
- application/octet-stream
|
||||||
actions:
|
actions:
|
||||||
- pull
|
- pull
|
||||||
reporting:
|
|
||||||
bugsnag:
|
|
||||||
apikey: BugsnagApiKey
|
|
||||||
http:
|
http:
|
||||||
clientcas:
|
clientcas:
|
||||||
- /path/to/ca.pem
|
- /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
|
// a string can be parsed into a Configuration struct with no storage parameters
|
||||||
func (suite *ConfigSuite) TestParseInmemory(c *check.C) {
|
func (suite *ConfigSuite) TestParseInmemory(c *check.C) {
|
||||||
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
||||||
suite.expectedConfig.Reporting = Reporting{}
|
|
||||||
suite.expectedConfig.Log.Fields = nil
|
suite.expectedConfig.Log.Fields = nil
|
||||||
suite.expectedConfig.Redis = struct {
|
suite.expectedConfig.Redis = struct {
|
||||||
Addr string `yaml:"addr,omitempty"`
|
Addr string `yaml:"addr,omitempty"`
|
||||||
|
@ -322,7 +313,6 @@ func (suite *ConfigSuite) TestParseIncomplete(c *check.C) {
|
||||||
suite.expectedConfig.Log.Fields = nil
|
suite.expectedConfig.Log.Fields = nil
|
||||||
suite.expectedConfig.Storage = Storage{"filesystem": Parameters{"rootdirectory": "/tmp/testroot"}}
|
suite.expectedConfig.Storage = Storage{"filesystem": Parameters{"rootdirectory": "/tmp/testroot"}}
|
||||||
suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}}
|
suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}}
|
||||||
suite.expectedConfig.Reporting = Reporting{}
|
|
||||||
suite.expectedConfig.Notifications = Notifications{}
|
suite.expectedConfig.Notifications = Notifications{}
|
||||||
suite.expectedConfig.HTTP.Headers = nil
|
suite.expectedConfig.HTTP.Headers = nil
|
||||||
suite.expectedConfig.Redis = struct {
|
suite.expectedConfig.Redis = struct {
|
||||||
|
@ -448,20 +438,6 @@ func (suite *ConfigSuite) TestParseInvalidLoglevel(c *check.C) {
|
||||||
c.Assert(err, check.NotNil)
|
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
|
// TestParseInvalidVersion validates that the parser will fail to parse a newer configuration
|
||||||
// version than the CurrentVersion
|
// version than the CurrentVersion
|
||||||
func (suite *ConfigSuite) TestParseInvalidVersion(c *check.C) {
|
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
|
// TestParseExtraneousVars validates that environment variables referring to
|
||||||
// nonexistent variables don't cause side effects.
|
// nonexistent variables don't cause side effects.
|
||||||
func (suite *ConfigSuite) TestParseExtraneousVars(c *check.C) {
|
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
|
// Environment variables which shouldn't set config items
|
||||||
os.Setenv("REGISTRY_DUCKS", "quack")
|
os.Setenv("REGISTRY_DUCKS", "quack")
|
||||||
|
@ -611,9 +583,6 @@ func copyConfig(config Configuration) *Configuration {
|
||||||
for k, v := range config.Storage.Parameters() {
|
for k, v := range config.Storage.Parameters() {
|
||||||
configCopy.Storage.setParameter(k, v)
|
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{}}
|
configCopy.Auth = Auth{config.Auth.Type(): Parameters{}}
|
||||||
for k, v := range config.Auth.Parameters() {
|
for k, v := range config.Auth.Parameters() {
|
||||||
|
|
|
@ -192,11 +192,6 @@ middleware:
|
||||||
- name: redirect
|
- name: redirect
|
||||||
options:
|
options:
|
||||||
baseurl: https://example.com/
|
baseurl: https://example.com/
|
||||||
reporting:
|
|
||||||
bugsnag:
|
|
||||||
apikey: bugsnagapikey
|
|
||||||
releasestage: bugsnagreleasestage
|
|
||||||
endpoint: bugsnagendpoint
|
|
||||||
http:
|
http:
|
||||||
addr: localhost:5000
|
addr: localhost:5000
|
||||||
prefix: /my/nested/registry/
|
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`. |
|
| `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`
|
## `http`
|
||||||
|
|
||||||
```none
|
```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/azcore v1.6.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.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/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/aws/aws-sdk-go v1.44.325
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
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-events v0.0.0-20190806004212-e31b211e4f1c
|
||||||
github.com/docker/go-metrics v0.0.1
|
github.com/docker/go-metrics v0.0.1
|
||||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
|
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/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/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // 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/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||||
|
@ -60,7 +55,6 @@ require (
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // 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/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/prometheus/client_golang v1.12.1 // indirect; updated to latest
|
github.com/prometheus/client_golang v1.12.1 // indirect; updated to latest
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
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/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/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/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-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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=
|
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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
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/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 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
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/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.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/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/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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
|
|
@ -12,10 +12,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
logrus_bugsnag "github.com/Shopify/logrus-bugsnag"
|
|
||||||
|
|
||||||
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
logstash "github.com/bshuster-repo/logrus-logstash-hook"
|
||||||
"github.com/bugsnag/bugsnag-go"
|
|
||||||
"github.com/docker/go-metrics"
|
"github.com/docker/go-metrics"
|
||||||
gorhandlers "github.com/gorilla/handlers"
|
gorhandlers "github.com/gorilla/handlers"
|
||||||
"github.com/sirupsen/logrus"
|
"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)
|
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
|
// inject a logger into the uuid library. warns us if there is a problem
|
||||||
// with uuid generation under low entropy.
|
// with uuid generation under low entropy.
|
||||||
uuid.Loggerf = dcontext.GetLogger(ctx).Warnf
|
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
|
// TODO(aaronl): The global scope of the health checks means NewRegistry
|
||||||
// can only be called once per process.
|
// can only be called once per process.
|
||||||
app.RegisterHealthChecks()
|
app.RegisterHealthChecks()
|
||||||
handler := configureReporting(app)
|
var handler http.Handler = app
|
||||||
handler = alive("/", handler)
|
handler = alive("/", handler)
|
||||||
handler = health.Handler(handler)
|
handler = health.Handler(handler)
|
||||||
handler = panicHandler(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
|
// configureLogging prepares the context with a logger using the
|
||||||
// configuration.
|
// configuration.
|
||||||
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
|
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
|
||||||
|
@ -410,32 +395,6 @@ func logLevel(level configuration.Loglevel) logrus.Level {
|
||||||
return l
|
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
|
// 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
|
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
|
||||||
// defined in config.yml.
|
// 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/shared
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/version
|
github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/version
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go/apps/public
|
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
|
# github.com/aws/aws-sdk-go v1.44.325
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/aws/aws-sdk-go/aws
|
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
|
# github.com/beorn7/perks v1.0.1
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/beorn7/perks/quantile
|
github.com/beorn7/perks/quantile
|
||||||
# github.com/bitly/go-simplejson v0.5.0
|
|
||||||
## explicit
|
|
||||||
# github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
# github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/bshuster-repo/logrus-logstash-hook
|
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
|
# github.com/cespare/xxhash/v2 v2.2.0
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/cespare/xxhash/v2
|
github.com/cespare/xxhash/v2
|
||||||
|
@ -265,8 +250,6 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||||
# github.com/mitchellh/mapstructure v1.1.2
|
# github.com/mitchellh/mapstructure v1.1.2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/mitchellh/mapstructure
|
github.com/mitchellh/mapstructure
|
||||||
# github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f
|
|
||||||
## explicit
|
|
||||||
# github.com/opencontainers/go-digest v1.0.0
|
# github.com/opencontainers/go-digest v1.0.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/opencontainers/go-digest
|
github.com/opencontainers/go-digest
|
||||||
|
|
Loading…
Reference in a new issue