From e4c9bff82bdb57afed6a2077e9695f1ec9dc7869 Mon Sep 17 00:00:00 2001 From: Michael Barrientos Date: Tue, 19 Nov 2019 16:57:35 -0800 Subject: [PATCH] Swtich to Sentry exception logging (#37) --- README.md | 2 +- cmd/root.go | 56 +-- go.mod | 5 +- go.sum | 114 +++-- .../github.com/airbrake/gobrake/.travis.yml | 20 - vendor/github.com/airbrake/gobrake/LICENSE | 27 -- vendor/github.com/airbrake/gobrake/Makefile | 5 - vendor/github.com/airbrake/gobrake/README.md | 84 ---- .../github.com/airbrake/gobrake/code_hunk.go | 74 --- vendor/github.com/airbrake/gobrake/filter.go | 113 ----- vendor/github.com/airbrake/gobrake/git.go | 254 ---------- vendor/github.com/airbrake/gobrake/gobrake.go | 16 - .../gobrake/internal/lrucache/lrucache.go | 91 ---- vendor/github.com/airbrake/gobrake/notice.go | 161 ------- .../github.com/airbrake/gobrake/notifier.go | 339 ------------- vendor/github.com/airbrake/gobrake/routes.go | 203 -------- vendor/github.com/airbrake/gobrake/stack.go | 104 ---- vendor/github.com/caio/go-tdigest/.gitignore | 2 - vendor/github.com/caio/go-tdigest/.travis.yml | 28 -- .../caio/go-tdigest/CONTRIBUTING.md | 43 -- vendor/github.com/caio/go-tdigest/Gopkg.lock | 41 -- vendor/github.com/caio/go-tdigest/Gopkg.toml | 21 - vendor/github.com/caio/go-tdigest/LICENSE | 21 - vendor/github.com/caio/go-tdigest/README.md | 95 ---- vendor/github.com/caio/go-tdigest/options.go | 51 -- vendor/github.com/caio/go-tdigest/rng.go | 40 -- .../caio/go-tdigest/serialization.go | 208 -------- vendor/github.com/caio/go-tdigest/summary.go | 206 -------- vendor/github.com/caio/go-tdigest/tdigest.go | 446 ------------------ .../github.com/getsentry/sentry-go/.craft.yml | 12 + .../github.com/getsentry/sentry-go/.gitignore | 6 + .../getsentry/sentry-go/.golangci.yml | 13 + .../getsentry/sentry-go/.travis.yml | 29 ++ .../getsentry/sentry-go/CHANGELOG.md | 125 +++++ .../getsentry/sentry-go/CONTRIBUTION.md | 41 ++ vendor/github.com/getsentry/sentry-go/LICENSE | 9 + .../getsentry/sentry-go/MIGRATION.md | 392 +++++++++++++++ .../github.com/getsentry/sentry-go/README.md | 108 +++++ .../github.com/getsentry/sentry-go/client.go | 429 +++++++++++++++++ vendor/github.com/getsentry/sentry-go/dsn.go | 185 ++++++++ vendor/github.com/getsentry/sentry-go/go.mod | 50 ++ vendor/github.com/getsentry/sentry-go/go.sum | 192 ++++++++ vendor/github.com/getsentry/sentry-go/hub.go | 336 +++++++++++++ .../getsentry/sentry-go/integrations.go | 376 +++++++++++++++ .../getsentry/sentry-go/interfaces.go | 180 +++++++ .../github.com/getsentry/sentry-go/scope.go | 309 ++++++++++++ .../github.com/getsentry/sentry-go/sentry.go | 122 +++++ .../getsentry/sentry-go/sourcereader.go | 69 +++ .../getsentry/sentry-go/stacktrace.go | 261 ++++++++++ .../getsentry/sentry-go/transport.go | 350 ++++++++++++++ vendor/github.com/getsentry/sentry-go/util.go | 34 ++ vendor/modules.txt | 7 +- 52 files changed, 3729 insertions(+), 2776 deletions(-) delete mode 100644 vendor/github.com/airbrake/gobrake/.travis.yml delete mode 100644 vendor/github.com/airbrake/gobrake/LICENSE delete mode 100644 vendor/github.com/airbrake/gobrake/Makefile delete mode 100644 vendor/github.com/airbrake/gobrake/README.md delete mode 100644 vendor/github.com/airbrake/gobrake/code_hunk.go delete mode 100644 vendor/github.com/airbrake/gobrake/filter.go delete mode 100644 vendor/github.com/airbrake/gobrake/git.go delete mode 100644 vendor/github.com/airbrake/gobrake/gobrake.go delete mode 100644 vendor/github.com/airbrake/gobrake/internal/lrucache/lrucache.go delete mode 100644 vendor/github.com/airbrake/gobrake/notice.go delete mode 100644 vendor/github.com/airbrake/gobrake/notifier.go delete mode 100644 vendor/github.com/airbrake/gobrake/routes.go delete mode 100644 vendor/github.com/airbrake/gobrake/stack.go delete mode 100644 vendor/github.com/caio/go-tdigest/.gitignore delete mode 100644 vendor/github.com/caio/go-tdigest/.travis.yml delete mode 100644 vendor/github.com/caio/go-tdigest/CONTRIBUTING.md delete mode 100644 vendor/github.com/caio/go-tdigest/Gopkg.lock delete mode 100644 vendor/github.com/caio/go-tdigest/Gopkg.toml delete mode 100644 vendor/github.com/caio/go-tdigest/LICENSE delete mode 100644 vendor/github.com/caio/go-tdigest/README.md delete mode 100644 vendor/github.com/caio/go-tdigest/options.go delete mode 100644 vendor/github.com/caio/go-tdigest/rng.go delete mode 100644 vendor/github.com/caio/go-tdigest/serialization.go delete mode 100644 vendor/github.com/caio/go-tdigest/summary.go delete mode 100644 vendor/github.com/caio/go-tdigest/tdigest.go create mode 100644 vendor/github.com/getsentry/sentry-go/.craft.yml create mode 100644 vendor/github.com/getsentry/sentry-go/.gitignore create mode 100644 vendor/github.com/getsentry/sentry-go/.golangci.yml create mode 100644 vendor/github.com/getsentry/sentry-go/.travis.yml create mode 100644 vendor/github.com/getsentry/sentry-go/CHANGELOG.md create mode 100644 vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md create mode 100644 vendor/github.com/getsentry/sentry-go/LICENSE create mode 100644 vendor/github.com/getsentry/sentry-go/MIGRATION.md create mode 100644 vendor/github.com/getsentry/sentry-go/README.md create mode 100644 vendor/github.com/getsentry/sentry-go/client.go create mode 100644 vendor/github.com/getsentry/sentry-go/dsn.go create mode 100644 vendor/github.com/getsentry/sentry-go/go.mod create mode 100644 vendor/github.com/getsentry/sentry-go/go.sum create mode 100644 vendor/github.com/getsentry/sentry-go/hub.go create mode 100644 vendor/github.com/getsentry/sentry-go/integrations.go create mode 100644 vendor/github.com/getsentry/sentry-go/interfaces.go create mode 100644 vendor/github.com/getsentry/sentry-go/scope.go create mode 100644 vendor/github.com/getsentry/sentry-go/sentry.go create mode 100644 vendor/github.com/getsentry/sentry-go/sourcereader.go create mode 100644 vendor/github.com/getsentry/sentry-go/stacktrace.go create mode 100644 vendor/github.com/getsentry/sentry-go/transport.go create mode 100644 vendor/github.com/getsentry/sentry-go/util.go diff --git a/README.md b/README.md index e1ca606..68e6d9c 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ secrets: `-y`, `--yes` assume "yes" to all prompts and run non-interactively ## Monitoring -Configure [Airbrake](https://airbrake.io/) for rotator by setting the `ENV`, `AIRBRAKE_PROJECT_ID`, and `AIRBRAKE_PROJECT_KEY` environment variables. +Configure [Sentry](https://getsentry.com/) for rotator by setting the `ENV`, `SENTRY_DSN` environment variables. ## Sources All sources must have the following fields in addition to any source-specific fields: diff --git a/cmd/root.go b/cmd/root.go index 75a37dd..945593c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,9 +2,9 @@ package cmd import ( "os" - "strconv" + "time" - "github.com/airbrake/gobrake" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -18,54 +18,38 @@ var rootCmd = &cobra.Command{ // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - airbrake, err := setUpAirbrake() + sentryEnabled, err := setUpSentry() if err != nil { - logrus.Warn(errors.Wrap(err, "unable to set up Airbrake notifier")) - } else { - defer func(airbrake *gobrake.Notifier) { - if err := airbrake.Close(); err != nil { - logrus.Error(errors.Wrap(err, "unable to close Airbrake notifier")) - } - }(airbrake) - defer airbrake.NotifyOnPanic() + logrus.Warn(errors.Wrap(err, "unable to set up Sentry notifier")) + } else if sentryEnabled { + defer sentry.Flush(time.Second * 5) + defer sentry.Recover() } if err := rootCmd.Execute(); err != nil { - if airbrake != nil { - airbrake.Notify(err, nil) - airbrake.Flush() + if sentryEnabled { + sentry.CaptureException(err) + sentry.Flush(time.Second * 5) } logrus.Fatal(err) } } -// reference: https://github.com/chanzuckerberg/mergebot/blob/fa0c67c2363f5f9e5a432a116ee82a294a9e5eea/cmd/run.go -func setUpAirbrake() (*gobrake.Notifier, error) { +func setUpSentry() (bool, error) { env := os.Getenv("ENV") if env == "" { - return nil, errors.New("please set the ENV variable to the name of the current execution environment (dev, stage, prod, etc)") - } - airEnv := os.Getenv("AIRBRAKE_PROJECT_ID") - airbrakeProjectID, err := strconv.ParseInt(airEnv, 10, 64) - if err != nil { - return nil, errors.Wrap(err, "unable to parse AIRBRAKE_PROJECT_ID variable to int64") + return false, errors.New("please set the ENV variable to the name of the current execution environment (dev, stage, prod, etc)") } + sentryDsn := os.Getenv("SENTRY_DSN") - var airbrake *gobrake.Notifier - if airbrakeProjectID != 0 { - airbrakeProjectKey := os.Getenv("AIRBRAKE_PROJECT_KEY") - if airbrakeProjectKey == "" { - return nil, errors.New("If you set AIRBRAKE_PROJECT_ID, you need to also set AIRBRAKE_PROJECT_KEY") - } - airbrake = gobrake.NewNotifierWithOptions(&gobrake.NotifierOptions{ - ProjectId: airbrakeProjectID, - ProjectKey: airbrakeProjectKey, + if sentryDsn != "" { + err := sentry.Init(sentry.ClientOptions{ + Dsn: sentryDsn, Environment: env, }) - airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice { - notice.Context["environment"] = env - return notice - }) + if err != nil { + return false, errors.Wrap(err, "sentry initialization failed") + } } - return airbrake, nil + return true, nil } diff --git a/go.mod b/go.mod index dd65733..bf19fba 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,19 @@ module github.com/chanzuckerberg/rotator go 1.13 require ( - github.com/airbrake/gobrake v3.7.4+incompatible github.com/aws/aws-sdk-go v1.25.14 github.com/caio/go-tdigest v2.3.0+incompatible // indirect github.com/chanzuckerberg/go-misc v0.0.0-20191016143922-52a18771c2dc github.com/fatih/color v1.7.0 + github.com/getsentry/sentry-go v0.3.0 github.com/golang/protobuf v1.3.2 // indirect github.com/google/uuid v1.1.1 github.com/hashicorp/go-multierror v1.0.0 github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.10 // indirect - github.com/onsi/ginkgo v1.8.0 // indirect - github.com/onsi/gomega v1.5.0 // indirect github.com/pkg/errors v0.8.1 github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad // build fails otherwise github.com/shuheiktgw/go-travis v0.2.4 diff --git a/go.sum b/go.sum index 996c758..0f8a008 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,22 @@ 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/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/airbrake/gobrake v3.7.4+incompatible h1:NHbD3yqK+qQagH42V1ZkCb9yXAMLswxI2UkQpkqjVvw= -github.com/airbrake/gobrake v3.7.4+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= +github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE= +github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-lambda-go v1.12.1/go.mod h1:z4ywteZ5WwbIEzG0tXizIAUlUwkTNNknX4upd5Z5XJM= github.com/aws/aws-sdk-go v1.23.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.14 h1:hEsU+cukBOQe1wRRuvEgG+y6AVCyS2eyHWuTefhGxTY= github.com/aws/aws-sdk-go v1.25.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/caio/go-tdigest v2.3.0+incompatible h1:zP6nR0nTSUzlSqqr7F/LhslPlSZX/fZeGmgmwj2cxxY= github.com/caio/go-tdigest v2.3.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/chanzuckerberg/go-misc v0.0.0-20191016143922-52a18771c2dc h1:Um5b35r3H4V+1PdI/53ZBDt9ilWUnXWIruMi7TG4aIM= github.com/chanzuckerberg/go-misc v0.0.0-20191016143922-52a18771c2dc/go.mod h1:yl4FcpRtJ0jkRJa0LeALu7aXbxTjNKp3FL37XjQ2Q/E= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -21,6 +24,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01/go.mod h1:ypD5nozFk9vcGw1ATYefw6jHe/jZP++Z15/+VTMcWhc= @@ -29,25 +34,33 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA= +github.com/getsentry/sentry-go v0.3.0 h1:6E+Oxq9CbT1kQrBPJ/RmWPqFBVS4CqU25RaMqeKnbs8= +github.com/getsentry/sentry-go v0.3.0/go.mod h1:Mrvr9TRhClLixedDiyFeucydQGOv4o7YQcW+Ry5vDdU= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= -github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -58,105 +71,139 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/honeycombio/libhoney-go v1.12.0/go.mod h1:jdLxh51fcBTy6XIpx1efuJmHePs2xUfVkw25lr+hsmg= github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk= github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/formBinder v5.0.0+incompatible/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce/go.mod h1:VER17o2JZqquOx41avolD/wMGQSFEFBKWmhag9/RQRY= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40/go.mod h1:PcaEvfvhGsqwXZ6S3CgCbmjcp+4UDUh2MIfF2ZEul8M= +github.com/kataras/iris v11.1.1+incompatible/go.mod h1:ki9XPua5SyAJbIxDdsssxevgGrbpBmmvoQmo/A0IodY= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.7.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= +github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0= github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad h1:EqOdoSJGI7CsBQczPcIgmpm3hJE7X8Hj3jrgI002whs= github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad/go.mod h1:B3ehdD1xPoWDKgrQgUaGk+m8H1xb1J5TyYDfKpKNeEE= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shuheiktgw/go-travis v0.2.4 h1:IAnh/Dyv7ql87qtJWUcvR5MM8e5iCDGoENl9VzpxAHc= github.com/shuheiktgw/go-travis v0.2.4/go.mod h1:RtODX49bvgHTvfzFvGEPFtU0dKVk0D3PyvUQR/63hT0= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -165,44 +212,37 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e h1:Io7mpb+aUAGF0MKxbyQ7HQl1VgB+cL6ZJZUFaFNqVV4= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846 h1:0oJP+9s5Z3MT6dym56c4f7nVeujVpL1QyD2Vp/bTql0= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20190802084026-e9a50d643359 h1:Brkpw2yBp/zIu1Y/RUro2pEBJW8+jIAEBiBejVt6/cY= gonum.org/v1/gonum v0.0.0-20190802084026-e9a50d643359/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/airbrake/gobrake/.travis.yml b/vendor/github.com/airbrake/gobrake/.travis.yml deleted file mode 100644 index aef525d..0000000 --- a/vendor/github.com/airbrake/gobrake/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -sudo: false -language: go - -go: - - 1.9.x - - 1.10.x - - 1.11.x - - tip - -matrix: - allow_failures: - - go: tip - -install: - - go get github.com/onsi/ginkgo - - go get github.com/onsi/gomega - - go get github.com/pkg/errors - - go get -u github.com/caio/go-tdigest - - go get -u github.com/gin-gonic/gin - - go get -u github.com/astaxie/beego diff --git a/vendor/github.com/airbrake/gobrake/LICENSE b/vendor/github.com/airbrake/gobrake/LICENSE deleted file mode 100644 index d64c10e..0000000 --- a/vendor/github.com/airbrake/gobrake/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 The Gobrake Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/airbrake/gobrake/Makefile b/vendor/github.com/airbrake/gobrake/Makefile deleted file mode 100644 index e5c018f..0000000 --- a/vendor/github.com/airbrake/gobrake/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -all: - go test ./... - go test ./... -short -race - env GOOS=linux GOARCH=386 go test ./... - go vet diff --git a/vendor/github.com/airbrake/gobrake/README.md b/vendor/github.com/airbrake/gobrake/README.md deleted file mode 100644 index ffce3fc..0000000 --- a/vendor/github.com/airbrake/gobrake/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Airbrake Golang Notifier [![Build Status](https://travis-ci.org/airbrake/gobrake.svg?branch=v2)](https://travis-ci.org/airbrake/gobrake) - - - -# Example - -```go -package main - -import ( - "errors" - - "github.com/airbrake/gobrake" -) - -var airbrake = gobrake.NewNotifierWithOptions(&gobrake.NotifierOptions{ - ProjectId: 123456, - ProjectKey: "FIXME", - Environment: "production", -}) - -func init() { - airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice { - notice.Params["user"] = map[string]string{ - "id": "1", - "username": "johnsmith", - "name": "John Smith", - } - return notice - }) -} - -func main() { - defer airbrake.Close() - defer airbrake.NotifyOnPanic() - - airbrake.Notify(errors.New("operation failed"), nil) -} -``` - -## Ignoring notices - -```go -airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice { - if notice.Context["environment"] == "development" { - // Ignore notices in development environment. - return nil - } - return notice -}) -``` - -## Setting severity - -[Severity](https://airbrake.io/docs/airbrake-faq/what-is-severity/) allows -categorizing how severe an error is. By default, it's set to `error`. To -redefine severity, simply overwrite `context/severity` of a notice object. For -example: - -```go -notice := airbrake.Notice("operation failed", nil, 3) -notice.Context["severity"] = "critical" -airbrake.Notify(notice, nil) -``` - -## Logging - -You can use [glog fork](https://github.com/airbrake/glog) to send your logs to Airbrake. - -## Sending requests stats - -In order to collect some basic requests stats you can instrument your application using `Notifier.NotifyRequest` API: - -```go -notifier.NotifyRequest(&gobrake.RequestInfo{ - Method: "GET", - Route: "/hello/:name", - StatusCode: http.StatusOK, - Start: startTime, - End: time.Now(), -}) -``` - -We also prepared HTTP middlewares for [Gin](examples/gin) and [Beego](examples/beego) users. diff --git a/vendor/github.com/airbrake/gobrake/code_hunk.go b/vendor/github.com/airbrake/gobrake/code_hunk.go deleted file mode 100644 index e1b6377..0000000 --- a/vendor/github.com/airbrake/gobrake/code_hunk.go +++ /dev/null @@ -1,74 +0,0 @@ -package gobrake - -import ( - "bufio" - "fmt" - "os" - "strconv" - - "github.com/airbrake/gobrake/internal/lrucache" -) - -var cache = lrucache.New(1000) - -func getCode(file string, line int) (map[int]string, error) { - cacheKey := file + strconv.Itoa(line) - - v, ok := cache.Get(cacheKey) - if ok { - switch v := v.(type) { - case error: - return nil, v - case map[int]string: - return v, nil - default: - return nil, fmt.Errorf("unsupported type=%T", v) - } - } - - lines, err := _getCode(file, line) - if err != nil { - cache.Set(cacheKey, err) - return nil, err - } - - return lines, nil -} - -func _getCode(file string, line int) (map[int]string, error) { - const nlines = 2 - const maxLineLen = 512 - - fd, err := os.Open(file) - if err != nil { - return nil, err - } - defer fd.Close() - - start := line - nlines - end := line + nlines - scanner := bufio.NewScanner(fd) - - var i int - lines := make(map[int]string, nlines) - for scanner.Scan() { - i++ - if i < start { - continue - } - if i > end { - break - } - line := scanner.Text() - if len(line) > maxLineLen { - line = line[:maxLineLen] - } - lines[i] = line - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return lines, nil -} diff --git a/vendor/github.com/airbrake/gobrake/filter.go b/vendor/github.com/airbrake/gobrake/filter.go deleted file mode 100644 index 8d34453..0000000 --- a/vendor/github.com/airbrake/gobrake/filter.go +++ /dev/null @@ -1,113 +0,0 @@ -package gobrake - -import ( - "fmt" - "path/filepath" - "regexp" - "strings" -) - -func newNotifierFilter(notifier *Notifier) func(*Notice) *Notice { - opt := notifier.opt - return func(notice *Notice) *Notice { - if opt.Environment != "" { - notice.Context["environment"] = opt.Environment - } - if opt.Revision != "" { - notice.Context["revision"] = opt.Revision - } - return notice - } -} - -func NewBlacklistKeysFilter(keys ...interface{}) func(*Notice) *Notice { - return func(notice *Notice) *Notice { - for _, key := range keys { - notice.Env = filterByKey(notice.Env, key) - notice.Context = filterByKey(notice.Context, key) - notice.Session = filterByKey(notice.Session, key) - } - - return notice - } -} - -func filterByKey(values map[string]interface{}, key interface{}) map[string]interface{} { - const filtered = "[Filtered]" - - switch key := key.(type) { - case string: - for k := range values { - if k == key { - values[k] = filtered - } - } - case *regexp.Regexp: - for k := range values { - if key.MatchString(k) { - values[k] = filtered - } - } - default: - panic(fmt.Errorf("unsupported blacklist key type: %T", key)) - } - - return values -} - -func gopathFilter(notice *Notice) *Notice { - s, ok := notice.Context["gopath"].(string) - if !ok { - return notice - } - - dirs := filepath.SplitList(s) - for i := range notice.Errors { - backtrace := notice.Errors[i].Backtrace - for j := range backtrace { - frame := &backtrace[j] - - for _, dir := range dirs { - dir = filepath.Join(dir, "src") - if strings.HasPrefix(frame.File, dir) { - frame.File = strings.Replace(frame.File, dir, "/GOPATH", 1) - break - } - } - } - } - - return notice -} - -func gitFilter(notice *Notice) *Notice { - rootDir, _ := notice.Context["rootDirectory"].(string) - if rootDir == "" { - return notice - } - - gitDir, ok := findGitDir(rootDir) - if !ok { - return notice - } - - info := getGitInfo(gitDir) - - if notice.Context == nil { - notice.Context = make(map[string]interface{}) - } - - if notice.Context["repository"] == nil && info.Repository != "" { - notice.Context["repository"] = info.Repository - } - - if notice.Context["revision"] == nil && info.Revision != "" { - notice.Context["revision"] = info.Revision - } - - if info.LastCheckout != nil { - notice.Context["lastCheckout"] = info.LastCheckout - } - - return notice -} diff --git a/vendor/github.com/airbrake/gobrake/git.go b/vendor/github.com/airbrake/gobrake/git.go deleted file mode 100644 index a853c3f..0000000 --- a/vendor/github.com/airbrake/gobrake/git.go +++ /dev/null @@ -1,254 +0,0 @@ -package gobrake - -import ( - "bufio" - "bytes" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "sync" - "time" -) - -type gitLog struct { - Username string `json:"username"` - Email string `json:"email"` - Revision string `json:"revision"` - Time time.Time `json:"time"` -} - -type gitInfo struct { - Repository string - Revision string - LastCheckout *gitLog -} - -var ( - gitInfosMu sync.RWMutex - gitInfos = make(map[string]*gitInfo) -) - -func getGitInfo(dir string) *gitInfo { - gitInfosMu.RLock() - info, ok := gitInfos[dir] - gitInfosMu.RUnlock() - - if ok { - return info - } - - gitInfosMu.Lock() - defer gitInfosMu.Unlock() - - info = new(gitInfo) - gitInfos[dir] = info - - repo, err := gitRepository(dir) - if err != nil { - logger.Printf("gitRepository dir=%q failed: %s", dir, err) - } else { - info.Repository = repo - } - - rev, err := gitRevision(dir) - if err != nil { - logger.Printf("gitRevision dir=%q failed: %s", dir, err) - } else { - info.Revision = rev - } - - lastCheckout, err := gitLastCheckout(dir) - if err != nil { - logger.Printf("gitLastCheckout dir=%q failed: %s", dir, err) - } else { - info.LastCheckout = lastCheckout - } - - return info -} - -func gitRepository(dir string) (string, error) { - cmd := exec.Command("git", "remote", "get-url", "origin") - cmd.Dir = dir - out, err := cmd.Output() - if err != nil { - return "", err - } - return string(trimnl(out)), nil -} - -// findGitDir returns first directory containing .git file checking the dir and parent dirs. -func findGitDir(dir string) (string, bool) { - dir, err := filepath.Abs(dir) - if err != nil || !exists(dir) { - return "", false - } - - for i := 0; i < 10; i++ { - path := filepath.Join(dir, ".git") - if exists(path) { - return dir, true - } - - if dir == "." || dir == "/" { - return "", false - } - - dir = filepath.Dir(dir) - } - - return "", false -} - -func gitRevision(dir string) (string, error) { - head, err := gitHead(dir) - if err != nil { - return "", err - } - - prefix := []byte("ref: ") - if !bytes.HasPrefix(head, prefix) { - return string(head), nil - } - head = head[len(prefix):] - - refFile := filepath.Join(dir, ".git", string(head)) - rev, err := ioutil.ReadFile(refFile) - if err == nil { - return string(trimnl(rev)), nil - } - - refsFile := filepath.Join(dir, ".git", "packed-refs") - fd, err := os.Open(refsFile) - if err != nil { - return "", err - } - - scanner := bufio.NewScanner(fd) - for scanner.Scan() { - b := scanner.Bytes() - if len(b) == 0 || b[0] == '#' || b[0] == '^' { - continue - } - - bs := bytes.Split(b, []byte{' '}) - if len(bs) != 2 { - continue - } - - if bytes.Equal(bs[1], head) { - return string(bs[0]), nil - } - } - if err := scanner.Err(); err != nil { - return "", err - } - - return "", fmt.Errorf("git revision for ref=%q not found", head) -} - -func gitHead(dir string) ([]byte, error) { - headFile := filepath.Join(dir, ".git", "HEAD") - b, err := ioutil.ReadFile(headFile) - if err != nil { - return nil, err - } - return trimnl(b), nil -} - -func trimnl(b []byte) []byte { - for _, c := range []byte{'\n', '\r'} { - if len(b) > 0 && b[len(b)-1] == c { - b = b[:len(b)-1] - } else { - break - } - } - return b -} - -func gitLastCheckout(dir string) (*gitLog, error) { - headFile := filepath.Join(dir, ".git", "logs", "HEAD") - line, err := lastCheckoutLine(headFile) - if err != nil { - return nil, err - } - - ind := strings.IndexByte(line, '\t') - if ind == -1 { - return nil, fmt.Errorf("tab not found") - } - line = line[:ind] - - parts := strings.Split(line, " ") - if len(parts) < 5 { - return nil, fmt.Errorf("can't parse %q", line) - } - author := parts[2 : len(parts)-2] - - utime, err := strconv.ParseInt(parts[len(parts)-2], 10, 64) - if err != nil { - return nil, err - } - - info := &gitLog{ - Revision: parts[1], - Time: time.Unix(utime, 0), - } - if email := cleanEmail(author[len(author)-1]); email != "" { - info.Email = email - author = author[:len(author)-1] - } - info.Username = strings.Join(author, " ") - - return info, nil -} - -func lastCheckoutLine(filename string) (string, error) { - fd, err := os.Open(filename) - if err != nil { - return "", err - } - - scanner := bufio.NewScanner(fd) - var lastCheckout string - for scanner.Scan() { - s := scanner.Text() - if strings.Contains(s, "\tclone: ") || - strings.Contains(s, "\tpull: ") || - strings.Contains(s, "\tcheckout: ") { - lastCheckout = s - } - } - if err := scanner.Err(); err != nil { - return "", err - } - - if lastCheckout == "" { - return "", fmt.Errorf("no clone, pull, or checkout entries") - } - return lastCheckout, nil -} - -func cleanEmail(s string) string { - if s == "" { - return "" - } - if s[0] == '<' && s[len(s)-1] == '>' { - return s[1 : len(s)-1] - } - return "" -} - -func exists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true -} diff --git a/vendor/github.com/airbrake/gobrake/gobrake.go b/vendor/github.com/airbrake/gobrake/gobrake.go deleted file mode 100644 index 23efe41..0000000 --- a/vendor/github.com/airbrake/gobrake/gobrake.go +++ /dev/null @@ -1,16 +0,0 @@ -package gobrake - -import ( - "log" - "os" -) - -var logger *log.Logger - -func init() { - SetLogger(log.New(os.Stderr, "gobrake: ", log.LstdFlags)) -} - -func SetLogger(l *log.Logger) { - logger = l -} diff --git a/vendor/github.com/airbrake/gobrake/internal/lrucache/lrucache.go b/vendor/github.com/airbrake/gobrake/internal/lrucache/lrucache.go deleted file mode 100644 index 053a6a8..0000000 --- a/vendor/github.com/airbrake/gobrake/internal/lrucache/lrucache.go +++ /dev/null @@ -1,91 +0,0 @@ -package lrucache - -import ( - "container/list" - "sync" - "time" -) - -type entry struct { - key string - value interface{} - addedAt time.Time -} - -type Cache struct { - mu sync.Mutex - - list *list.List - table map[string]*list.Element - - maxLen int -} - -func New(maxLen int) *Cache { - return &Cache{ - list: list.New(), - table: make(map[string]*list.Element, maxLen), - - maxLen: maxLen, - } -} - -func (c *Cache) Get(key string) (interface{}, bool) { - return c.get(key) -} - -func (c *Cache) get(key string) (interface{}, bool) { - c.mu.Lock() - - el := c.table[key] - if el == nil { - c.mu.Unlock() - return nil, false - } - - entry := el.Value.(*entry) - c.list.MoveToFront(el) - value := entry.value - c.mu.Unlock() - return value, true -} - -func (c *Cache) Set(key string, value interface{}) { - c.mu.Lock() - if el := c.table[key]; el != nil { - entry := el.Value.(*entry) - entry.value = value - c.promote(el, entry) - } else { - c.addNew(key, value) - } - c.mu.Unlock() -} - -func (c *Cache) addNew(key string, value interface{}) { - newEntry := &entry{ - key: key, - value: value, - addedAt: time.Now(), - } - element := c.list.PushFront(newEntry) - c.table[key] = element - c.check() -} - -func (c *Cache) promote(el *list.Element, entry *entry) { - entry.addedAt = time.Now() - c.list.MoveToFront(el) -} - -func (c *Cache) deleteElement(el *list.Element) { - c.list.Remove(el) - delete(c.table, el.Value.(*entry).key) -} - -func (c *Cache) check() { - for c.list.Len() > c.maxLen { - el := c.list.Back() - c.deleteElement(el) - } -} diff --git a/vendor/github.com/airbrake/gobrake/notice.go b/vendor/github.com/airbrake/gobrake/notice.go deleted file mode 100644 index b8398f1..0000000 --- a/vendor/github.com/airbrake/gobrake/notice.go +++ /dev/null @@ -1,161 +0,0 @@ -package gobrake - -import ( - "fmt" - "net/http" - "os" - "runtime" - "strings" - "sync" - - "github.com/pkg/errors" -) - -var defaultContextOnce sync.Once -var defaultContext map[string]interface{} - -func getDefaultContext() map[string]interface{} { - defaultContextOnce.Do(func() { - defaultContext = map[string]interface{}{ - "notifier": map[string]interface{}{ - "name": "gobrake", - "version": "3.4.0", - "url": "https://github.com/airbrake/gobrake", - }, - - "language": runtime.Version(), - "os": runtime.GOOS, - "architecture": runtime.GOARCH, - } - - if s, err := os.Hostname(); err == nil { - defaultContext["hostname"] = s - } - - if wd, err := os.Getwd(); err == nil { - defaultContext["rootDirectory"] = wd - } - - if s := os.Getenv("GOPATH"); s != "" { - defaultContext["gopath"] = s - } - }) - return defaultContext -} - -type Error struct { - Type string `json:"type"` - Message string `json:"message"` - Backtrace []StackFrame `json:"backtrace"` -} - -type StackFrame struct { - File string `json:"file"` - Line int `json:"line"` - Func string `json:"function"` - Code map[int]string `json:"code,omitempty"` -} - -type Notice struct { - Id string `json:"-"` // id returned by SendNotice - Error error `json:"-"` // error returned by SendNotice - - Errors []Error `json:"errors"` - Context map[string]interface{} `json:"context"` - Env map[string]interface{} `json:"environment"` - Session map[string]interface{} `json:"session"` - Params map[string]interface{} `json:"params"` -} - -func (n *Notice) String() string { - if len(n.Errors) == 0 { - return "Notice" - } - e := n.Errors[0] - return fmt.Sprintf("Notice<%s: %s>", e.Type, e.Message) -} - -func (n *Notice) SetRequest(req *http.Request) { - n.Context["url"] = req.URL.String() - n.Context["httpMethod"] = req.Method - if ua := req.Header.Get("User-Agent"); ua != "" { - n.Context["userAgent"] = ua - } - n.Context["userAddr"] = remoteAddr(req) - - for k, v := range req.Header { - if len(v) == 1 { - n.Env[k] = v[0] - } else { - n.Env[k] = v - } - } -} - -func remoteAddr(req *http.Request) string { - if s := req.Header.Get("X-Forwarded-For"); s != "" { - parts := strings.Split(s, ",") - return parts[0] - } - - if s := req.Header.Get("X-Real-Ip"); s != "" { - return s - } - - parts := strings.Split(req.RemoteAddr, ":") - return parts[0] -} - -func NewNotice(e interface{}, req *http.Request, depth int) *Notice { - notice, ok := e.(*Notice) - if ok { - return notice - } - - typeName := getTypeName(e) - packageName, backtrace := getBacktrace(e, depth) - - for i := range backtrace { - frame := &backtrace[i] - code, err := getCode(frame.File, frame.Line) - if err != nil { - if !os.IsNotExist(err) { - logger.Printf("getCode file=%q line=%d failed: %s", - frame.File, frame.Line, err) - } - continue - } - frame.Code = code - } - - notice = &Notice{ - Errors: []Error{{ - Type: typeName, - Message: fmt.Sprint(e), - Backtrace: backtrace, - }}, - Context: make(map[string]interface{}), - Env: make(map[string]interface{}), - Session: make(map[string]interface{}), - Params: make(map[string]interface{}), - } - - for k, v := range getDefaultContext() { - notice.Context[k] = v - } - notice.Context["component"] = packageName - - if req != nil { - notice.SetRequest(req) - } - - return notice -} - -// getTypeName returns the type name of e. -func getTypeName(e interface{}) string { - if err, ok := e.(error); ok { - e = errors.Cause(err) - } - return fmt.Sprintf("%T", e) -} diff --git a/vendor/github.com/airbrake/gobrake/notifier.go b/vendor/github.com/airbrake/gobrake/notifier.go deleted file mode 100644 index 184d5a0..0000000 --- a/vendor/github.com/airbrake/gobrake/notifier.go +++ /dev/null @@ -1,339 +0,0 @@ -package gobrake - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "net" - "net/http" - "os" - "regexp" - "runtime" - "strconv" - "sync" - "sync/atomic" - "time" -) - -const waitTimeout = 5 * time.Second - -const httpEnhanceYourCalm = 420 -const httpStatusTooManyRequests = 429 - -const maxNoticeLen = 64 * 1024 - -var ( - errClosed = errors.New("gobrake: notifier is closed") - errQueueFull = errors.New("gobrake: queue is full (error is dropped)") - errUnauthorized = errors.New("gobrake: unauthorized: invalid project id or key") - errAccountRateLimited = errors.New("gobrake: account is rate limited") - errIPRateLimited = errors.New("gobrake: IP is rate limited") - errNoticeTooBig = errors.New("gobrake: notice exceeds 64KB max size limit") -) - -var ( - httpClientOnce sync.Once - httpClient *http.Client -) - -func defaultHTTPClient() *http.Client { - httpClientOnce.Do(func() { - httpClient = &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 15 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: &tls.Config{ - ClientSessionCache: tls.NewLRUClientSessionCache(1024), - }, - MaxIdleConnsPerHost: 10, - ResponseHeaderTimeout: 10 * time.Second, - }, - Timeout: 10 * time.Second, - } - }) - return httpClient -} - -var buffers = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, -} - -type filter func(*Notice) *Notice - -type NotifierOptions struct { - // Airbrake project id. - ProjectId int64 - // Airbrake project key. - ProjectKey string - // Airbrake host name. Default is https://airbrake.io. - Host string - - // Environment such as production or development. - Environment string - // Git revision. Default is SOURCE_VERSION on Heroku. - Revision string - // List of keys containing sensitive information that must be filtered out. - // Default is password, secret. - KeysBlacklist []interface{} - - // http.Client that is used to interact with Airbrake API. - HTTPClient *http.Client -} - -func (opt *NotifierOptions) init() { - if opt.Host == "" { - opt.Host = "https://api.airbrake.io" - } - - if opt.Revision == "" { - // https://devcenter.heroku.com/changelog-items/630 - opt.Revision = os.Getenv("SOURCE_VERSION") - } - - if opt.KeysBlacklist == nil { - opt.KeysBlacklist = []interface{}{ - regexp.MustCompile("password"), - regexp.MustCompile("secret"), - } - } - - if opt.HTTPClient == nil { - opt.HTTPClient = defaultHTTPClient() - } -} - -type Notifier struct { - opt *NotifierOptions - createNoticeURL string - - filters []filter - - inFlight int32 // atomic - limit chan struct{} - wg sync.WaitGroup - - routes *routeStats - - rateLimitReset uint32 // atomic - _closed uint32 // atomic -} - -func NewNotifierWithOptions(opt *NotifierOptions) *Notifier { - opt.init() - - n := &Notifier{ - opt: opt, - createNoticeURL: fmt.Sprintf("%s/api/v3/projects/%d/notices", - opt.Host, opt.ProjectId), - - limit: make(chan struct{}, 2*runtime.NumCPU()), - - routes: newRouteStats(opt), - } - - n.AddFilter(newNotifierFilter(n)) - n.AddFilter(gopathFilter) - n.AddFilter(gitFilter) - - if len(opt.KeysBlacklist) > 0 { - n.AddFilter(NewBlacklistKeysFilter(opt.KeysBlacklist...)) - } - - return n -} - -func NewNotifier(projectId int64, projectKey string) *Notifier { - return NewNotifierWithOptions(&NotifierOptions{ - ProjectId: projectId, - ProjectKey: projectKey, - }) -} - -// AddFilter adds filter that can change notice or ignore it by returning nil. -func (n *Notifier) AddFilter(fn func(*Notice) *Notice) { - n.filters = append(n.filters, fn) -} - -// Notify notifies Airbrake about the error. -func (n *Notifier) Notify(e interface{}, req *http.Request) { - notice := n.Notice(e, req, 1) - n.SendNoticeAsync(notice) -} - -// Notice returns Aibrake notice created from error and request. depth -// determines which call frame to use when constructing backtrace. -func (n *Notifier) Notice(err interface{}, req *http.Request, depth int) *Notice { - return NewNotice(err, req, depth+3) -} - -type sendResponse struct { - Id string `json:"id"` -} - -// SendNotice sends notice to Airbrake. -func (n *Notifier) SendNotice(notice *Notice) (string, error) { - if n.closed() { - return "", errClosed - } - return n.sendNotice(notice) -} - -func (n *Notifier) sendNotice(notice *Notice) (string, error) { - for _, fn := range n.filters { - notice = fn(notice) - if notice == nil { - // Notice is ignored. - return "", nil - } - } - - if time.Now().Unix() < int64(atomic.LoadUint32(&n.rateLimitReset)) { - return "", errIPRateLimited - } - - buf := buffers.Get().(*bytes.Buffer) - defer buffers.Put(buf) - - buf.Reset() - err := json.NewEncoder(buf).Encode(notice) - if err != nil { - return "", err - } - - if buf.Len() > maxNoticeLen { - return "", errNoticeTooBig - } - - req, err := http.NewRequest("POST", n.createNoticeURL, buf) - if err != nil { - return "", err - } - - req.Header.Set("Authorization", "Bearer "+n.opt.ProjectKey) - req.Header.Set("Content-Type", "application/json") - resp, err := n.opt.HTTPClient.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - buf.Reset() - _, err = buf.ReadFrom(resp.Body) - if err != nil { - return "", err - } - - if resp.StatusCode >= 200 && resp.StatusCode < 300 { - var sendResp sendResponse - err = json.NewDecoder(buf).Decode(&sendResp) - if err != nil { - return "", err - } - return sendResp.Id, nil - } - - switch resp.StatusCode { - case http.StatusUnauthorized: - return "", errUnauthorized - case httpStatusTooManyRequests: - delayStr := resp.Header.Get("X-RateLimit-Delay") - delay, err := strconv.ParseInt(delayStr, 10, 64) - if err == nil { - atomic.StoreUint32(&n.rateLimitReset, uint32(time.Now().Unix()+delay)) - } - return "", errIPRateLimited - case httpEnhanceYourCalm: - return "", errAccountRateLimited - } - - err = fmt.Errorf("got unexpected response status=%q", resp.Status) - logger.Printf("SendNotice failed reporting notice=%q: %s", notice, err) - return "", err -} - -// SendNoticeAsync is like SendNotice, but sends notice asynchronously. -// Pending notices can be flushed with Flush. -func (n *Notifier) SendNoticeAsync(notice *Notice) { - if n.closed() { - notice.Error = errClosed - return - } - - inFlight := atomic.AddInt32(&n.inFlight, 1) - if inFlight > 1000 { - atomic.AddInt32(&n.inFlight, -1) - notice.Error = errQueueFull - return - } - - n.wg.Add(1) - go func() { - n.limit <- struct{}{} - - notice.Id, notice.Error = n.sendNotice(notice) - atomic.AddInt32(&n.inFlight, -1) - n.wg.Done() - - <-n.limit - }() -} - -// NotifyOnPanic notifies Airbrake about the panic and should be used -// with defer statement. -func (n *Notifier) NotifyOnPanic() { - if v := recover(); v != nil { - notice := n.Notice(v, nil, 3) - notice.Context["severity"] = "critical" - n.SendNotice(notice) - panic(v) - } -} - -// Flush waits for pending requests to finish. -func (n *Notifier) Flush() { - n.waitTimeout(waitTimeout) -} - -func (n *Notifier) Close() error { - return n.CloseTimeout(waitTimeout) -} - -// CloseTimeout waits for pending requests to finish and then closes the notifier. -func (n *Notifier) CloseTimeout(timeout time.Duration) error { - if !atomic.CompareAndSwapUint32(&n._closed, 0, 1) { - return nil - } - return n.waitTimeout(timeout) -} - -func (n *Notifier) closed() bool { - return atomic.LoadUint32(&n._closed) == 1 -} - -func (n *Notifier) waitTimeout(timeout time.Duration) error { - done := make(chan struct{}) - go func() { - n.wg.Wait() - close(done) - }() - - select { - case <-done: - return nil - case <-time.After(timeout): - return fmt.Errorf("Wait timed out after %s", timeout) - } -} - -// NotifyRequest notifies Airbrake about the request. -func (n *Notifier) NotifyRequest(req *RequestInfo) error { - return n.routes.NotifyRequest(req) -} diff --git a/vendor/github.com/airbrake/gobrake/routes.go b/vendor/github.com/airbrake/gobrake/routes.go deleted file mode 100644 index bc8f454..0000000 --- a/vendor/github.com/airbrake/gobrake/routes.go +++ /dev/null @@ -1,203 +0,0 @@ -package gobrake - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "sync" - "time" - - tdigest "github.com/caio/go-tdigest" -) - -const flushPeriod = 15 * time.Second - -type RequestInfo struct { - Method string - Route string - StatusCode int - Start time.Time - End time.Time -} - -type routeKey struct { - Method string `json:"method"` - Route string `json:"route"` - StatusCode int `json:"statusCode"` - Time time.Time `json:"time"` -} - -type routeStat struct { - mu sync.Mutex - Count int `json:"count"` - Sum float64 `json:"sum"` - Sumsq float64 `json:"sumsq"` - TDigest []byte `json:"tdigest"` - td *tdigest.TDigest -} - -func (s *routeStat) Add(ms float64) error { - if s.td == nil { - td, err := tdigest.New(tdigest.Compression(20)) - if err != nil { - return err - } - s.td = td - } - - s.Count++ - s.Sum += ms - s.Sumsq += ms * ms - return s.td.Add(ms) -} - -type routeKeyStat struct { - routeKey - *routeStat -} - -// routeStats aggregates information about requests and periodically sends -// collected data to Airbrake. -type routeStats struct { - opt *NotifierOptions - apiURL string - - flushTimer *time.Timer - addWG *sync.WaitGroup - - mu sync.Mutex - m map[routeKey]*routeStat -} - -func newRouteStats(opt *NotifierOptions) *routeStats { - return &routeStats{ - opt: opt, - apiURL: fmt.Sprintf("%s/api/v5/projects/%d/routes-stats", - opt.Host, opt.ProjectId), - } -} - -func (s *routeStats) init() { - if s.flushTimer == nil { - s.flushTimer = time.AfterFunc(flushPeriod, s.flush) - s.addWG = new(sync.WaitGroup) - s.m = make(map[routeKey]*routeStat) - } -} - -func (s *routeStats) flush() { - s.mu.Lock() - - s.flushTimer = nil - addWG := s.addWG - s.addWG = nil - m := s.m - s.m = nil - - s.mu.Unlock() - - addWG.Wait() - err := s.send(m) - if err != nil { - logger.Printf("routeStats.send failed: %s", err) - } -} - -type routesStatsJSONRequest struct { - Routes []routeKeyStat `json:"routes"` -} - -func (s *routeStats) send(m map[routeKey]*routeStat) error { - var routes []routeKeyStat - for k, v := range m { - err := v.td.Compress() - if err != nil { - return err - } - - b, err := v.td.AsBytes() - if err != nil { - return err - } - v.TDigest = b - - routes = append(routes, routeKeyStat{ - routeKey: k, - routeStat: v, - }) - } - - jsonReq := routesStatsJSONRequest{ - Routes: routes, - } - - buf := buffers.Get().(*bytes.Buffer) - defer buffers.Put(buf) - - buf.Reset() - err := json.NewEncoder(buf).Encode(jsonReq) - if err != nil { - return err - } - - req, err := http.NewRequest("PUT", s.apiURL, buf) - if err != nil { - return err - } - - req.Header.Set("Authorization", "Bearer "+s.opt.ProjectKey) - req.Header.Set("Content-Type", "application/json") - resp, err := s.opt.HTTPClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - buf.Reset() - _, err = buf.ReadFrom(resp.Body) - if err != nil { - return err - } - - if resp.StatusCode >= 200 && resp.StatusCode < 300 { - return nil - } - - switch resp.StatusCode { - case http.StatusUnauthorized: - return errUnauthorized - } - - err = fmt.Errorf("got unexpected response status=%q", resp.Status) - return err -} - -func (s *routeStats) NotifyRequest(req *RequestInfo) error { - key := routeKey{ - Method: req.Method, - Route: req.Route, - StatusCode: req.StatusCode, - Time: req.Start.UTC().Truncate(time.Minute), - } - - s.mu.Lock() - s.init() - stat, ok := s.m[key] - if !ok { - stat = &routeStat{} - s.m[key] = stat - } - addWG := s.addWG - s.addWG.Add(1) - s.mu.Unlock() - - ms := float64(req.End.Sub(req.Start)) / float64(time.Millisecond) - - stat.mu.Lock() - err := stat.Add(ms) - addWG.Done() - stat.mu.Unlock() - - return err -} diff --git a/vendor/github.com/airbrake/gobrake/stack.go b/vendor/github.com/airbrake/gobrake/stack.go deleted file mode 100644 index 108d609..0000000 --- a/vendor/github.com/airbrake/gobrake/stack.go +++ /dev/null @@ -1,104 +0,0 @@ -package gobrake - -import ( - "runtime" - "strings" - - "github.com/pkg/errors" -) - -// getBacktrace returns the stacktrace associated with e. If e is an -// error from the errors package its stacktrace is extracted, otherwise -// the current stacktrace is collected end returned. -func getBacktrace(e interface{}, skip int) (string, []StackFrame) { - if err, ok := e.(stackTracer); ok { - return backtraceFromErrorWithStackTrace(err) - } - - const depth = 32 - var pcs [depth]uintptr - n := runtime.Callers(skip+1, pcs[:]) - ff := runtime.CallersFrames(pcs[:n]) - - var firstPkg string - frames := make([]StackFrame, 0) - for { - f, ok := ff.Next() - if !ok { - break - } - - pkg, fn := splitPackageFuncName(f.Function) - if firstPkg == "" && pkg != "runtime" { - firstPkg = pkg - } - - if stackFilter(pkg, fn, f.File, f.Line) { - frames = frames[:0] - continue - } - - frames = append(frames, StackFrame{ - File: f.File, - Line: f.Line, - Func: fn, - }) - } - - return firstPkg, frames -} - -func splitPackageFuncName(funcName string) (string, string) { - var packageName string - if ind := strings.LastIndex(funcName, "/"); ind > 0 { - packageName += funcName[:ind+1] - funcName = funcName[ind+1:] - } - if ind := strings.Index(funcName, "."); ind > 0 { - packageName += funcName[:ind] - funcName = funcName[ind+1:] - } - return packageName, funcName -} - -func stackFilter(packageName, funcName string, file string, line int) bool { - return packageName == "runtime" && funcName == "panic" -} - -// stackTraces returns the stackTrace of an error. -// It is part of the errors package public interface. -type stackTracer interface { - StackTrace() errors.StackTrace -} - -// backtraceFromErrorWithStackTrace extracts the stacktrace from e. -func backtraceFromErrorWithStackTrace(e stackTracer) (string, []StackFrame) { - stackTrace := e.StackTrace() - var pcs []uintptr - for _, f := range stackTrace { - pcs = append(pcs, uintptr(f)) - } - - ff := runtime.CallersFrames(pcs) - var firstPkg string - frames := make([]StackFrame, 0) - for { - f, ok := ff.Next() - if !ok { - break - } - - pkg, fn := splitPackageFuncName(f.Function) - if firstPkg == "" { - firstPkg = pkg - } - - frames = append(frames, StackFrame{ - File: f.File, - Line: f.Line, - Func: fn, - }) - } - - return firstPkg, frames -} diff --git a/vendor/github.com/caio/go-tdigest/.gitignore b/vendor/github.com/caio/go-tdigest/.gitignore deleted file mode 100644 index f9f915f..0000000 --- a/vendor/github.com/caio/go-tdigest/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -go-tdigest.test diff --git a/vendor/github.com/caio/go-tdigest/.travis.yml b/vendor/github.com/caio/go-tdigest/.travis.yml deleted file mode 100644 index c8fc8fa..0000000 --- a/vendor/github.com/caio/go-tdigest/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: go - -env: - - DEP_VERSION="0.3.2" - -before_install: - - curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep - - chmod +x $GOPATH/bin/dep - -install: - - dep ensure - -go: - - "1.9" - - "1.10" - - "1.11" - - tip - -matrix: - allow_failures: - - go: tip - fast_finish: true - -before_script: - - go vet ./... - -script: - - go test -v diff --git a/vendor/github.com/caio/go-tdigest/CONTRIBUTING.md b/vendor/github.com/caio/go-tdigest/CONTRIBUTING.md deleted file mode 100644 index 9cbc33f..0000000 --- a/vendor/github.com/caio/go-tdigest/CONTRIBUTING.md +++ /dev/null @@ -1,43 +0,0 @@ -# Contributing - -First and foremost: **thank you very much** for your interest in this -project. Feel free to skip all this and open your issue / pull request -if reading contribution guidelines is too much for you at this point. -We value your contribution a lot more than we value your ability to -follow rules (and thankfully we can afford to take this approach given -this project's demand). - -Any kind of contribution is welcome. We can always use better docs and -tests (and code, of course). If you think you can improve this project -in any dimension _let's talk_ :-) - -## Guidelines - -Be kind and respectful in all your interactions with people inside -(outside too!) this community; There is no excuse for not showing -basic decency. Sarcasm and generally unconstructive remarks are **not -welcome**. - -### Issues - -When opening and interacting with issues please: - -- Be as clear as possible -- Provide examples if you can - -### Pull Requests - -We expect that pull requests: - -- Have [good commit messages][commits] -- Contain tests for new features -- Target and can be cleanly merged with the `master` branch -- Pass the tests (TravisCI will run them automatically but `go test` is - your friend) - -[commits]: https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines - -### Project Management - -Don't bother with labels, milestones, assignments, etc. We don't make -use of those. diff --git a/vendor/github.com/caio/go-tdigest/Gopkg.lock b/vendor/github.com/caio/go-tdigest/Gopkg.lock deleted file mode 100644 index 65bf906..0000000 --- a/vendor/github.com/caio/go-tdigest/Gopkg.lock +++ /dev/null @@ -1,41 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:cf63454c1e81409484ded047413228de0f7a3031f0fcd36d4e1db7620c3c7d1b" - name = "github.com/leesper/go_rng" - packages = ["."] - pruneopts = "" - revision = "5344a9259b21627d94279721ab1f27eb029194e7" - -[[projects]] - branch = "master" - digest = "1:ad6d9b2cce40c7c44952d49a6a324a2110db43b4279d9e599db74e45de5ae80c" - name = "gonum.org/v1/gonum" - packages = [ - "blas", - "blas/blas64", - "blas/gonum", - "floats", - "internal/asm/c128", - "internal/asm/f32", - "internal/asm/f64", - "internal/math32", - "lapack", - "lapack/gonum", - "lapack/lapack64", - "mat", - "stat", - ] - pruneopts = "" - revision = "f0982070f509ee139841ca385c44dc22a77c8da8" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/leesper/go_rng", - "gonum.org/v1/gonum/stat", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/caio/go-tdigest/Gopkg.toml b/vendor/github.com/caio/go-tdigest/Gopkg.toml deleted file mode 100644 index 323002c..0000000 --- a/vendor/github.com/caio/go-tdigest/Gopkg.toml +++ /dev/null @@ -1,21 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" diff --git a/vendor/github.com/caio/go-tdigest/LICENSE b/vendor/github.com/caio/go-tdigest/LICENSE deleted file mode 100644 index f5f0744..0000000 --- a/vendor/github.com/caio/go-tdigest/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Caio Romão Costa Nascimento - -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. diff --git a/vendor/github.com/caio/go-tdigest/README.md b/vendor/github.com/caio/go-tdigest/README.md deleted file mode 100644 index ae93222..0000000 --- a/vendor/github.com/caio/go-tdigest/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# T-Digest - -A fast map-reduce and parallel streaming friendly data-structure for accurate -quantile approximation. - -This package provides an implementation of Ted Dunning's t-digest data -structure in Go. - -[![Build Status](https://travis-ci.org/caio/go-tdigest.svg?branch=master)](https://travis-ci.org/caio/go-tdigest) -[![GoDoc](https://godoc.org/github.com/caio/go-tdigest?status.svg)](http://godoc.org/github.com/caio/go-tdigest) -[![Go Report Card](https://goreportcard.com/badge/github.com/caio/go-tdigest)](https://goreportcard.com/report/github.com/caio/go-tdigest) - -## Project Status - -This project is actively maintained. We are happy to collaborate on features -and issues if/when they arrive. - -## Installation - -Our releases are tagged and signed following the [Semantic Versioning][semver] -scheme. If you are using a dependency manager such as [dep][], the recommended -way to is go about your business normally: - - go get github.com/caio/go-tdigest - -Otherwise we recommend to use the following so that you don't risk breaking -your build because of an API change: - - go get gopkg.in/caio/go-tdigest.v2 - -[semver]: http://semver.org/ -[dep]: https://github.com/golang/dep - -## Example Usage - -```go -package main - -import ( - "fmt" - "math/rand" - - "github.com/caio/go-tdigest" -) - -func main() { - // Analogue to tdigest.New(tdigest.Compression(100)) - t, _ := tdigest.New() - - for i := 0; i < 10000; i++ { - // Analogue to t.AddWeighted(rand.Float64(), 1) - t.Add(rand.Float64()) - } - - fmt.Printf("p(.5) = %.6f\n", t.Quantile(0.5)) - fmt.Printf("CDF(Quantile(.5)) = %.6f\n", t.CDF(t.Quantile(0.5))) -} -``` - -## Configuration - -You can configure your digest upon creation with options documented -at [options.go](options.go). Example: - -```go -// Construct a digest with compression=200 and its own -// (thread-unsafe) RNG seeded with 0xCA10: -digest, _ := tdigest.New( - tdigest.Compression(200), - tdigest.LocalRandomNumberGenerator(0xCA10), -) -``` - -## Porting Existing Code to the v2 API - -It's very easy to migrate to the new API: - -- Replace `tdigest.New(100)` with `tdigest.New()` -- Replace `tdigest.New(number)` with `tdigest.New(tdigest.Compression(number))` -- Replace `Add(x,1)` with `Add(x)` -- Replace `Add(x, weight)` with `AddWeighted(x, weight)` -- Remove any use of `tdigest.Len()` (or [open an issue][issues]) - -[issues]: https://github.com/caio/go-tdigest/issues/new - -## References - -This is a port of the [reference][1] implementation with some ideas borrowed -from the [python version][2]. If you wanna get a quick grasp of how it works -and why it's useful, [this video and companion article is pretty helpful][3]. - -[1]: https://github.com/tdunning/t-digest -[2]: https://github.com/CamDavidsonPilon/tdigest -[3]: https://www.mapr.com/blog/better-anomaly-detection-t-digest-whiteboard-walkthrough - diff --git a/vendor/github.com/caio/go-tdigest/options.go b/vendor/github.com/caio/go-tdigest/options.go deleted file mode 100644 index ae996cf..0000000 --- a/vendor/github.com/caio/go-tdigest/options.go +++ /dev/null @@ -1,51 +0,0 @@ -package tdigest - -import "errors" - -type tdigestOption func(*TDigest) error - -// Compression sets the digest compression -// -// The compression parameter rules the threshold in which samples are -// merged together - the more often distinct samples are merged the more -// precision is lost. Compression should be tuned according to your data -// distribution, but a value of 100 (the default) is often good enough. -// -// A higher compression value means holding more centroids in memory -// (thus: better precision), which means a bigger serialization payload, -// higher memory footprint and slower addition of new samples. -// -// Compression must be a value greater of equal to 1, will yield an -// error otherwise. -func Compression(compression uint32) tdigestOption { // nolint - return func(t *TDigest) error { - if compression < 1 { - return errors.New("Compression should be >= 1") - } - t.compression = float64(compression) - return nil - } -} - -// RandomNumberGenerator sets the RNG to be used internally -// -// This allows changing which random number source is used when using -// the TDigest structure (rngs are used when deciding which candidate -// centroid to merge with and when compressing or merging with -// another digest for it increases accuracy). This functionality is -// particularly useful for testing or when you want to disconnect -// your sample collection from the (default) shared random source -// to minimize lock contention. -func RandomNumberGenerator(rng RNG) tdigestOption { // nolint - return func(t *TDigest) error { - t.rng = rng - return nil - } -} - -// LocalRandomNumberGenerator makes the TDigest use the default -// `math/random` functions but with an unshared source that is -// seeded with the given `seed` parameter. -func LocalRandomNumberGenerator(seed int64) tdigestOption { // nolint - return RandomNumberGenerator(newLocalRNG(seed)) -} diff --git a/vendor/github.com/caio/go-tdigest/rng.go b/vendor/github.com/caio/go-tdigest/rng.go deleted file mode 100644 index 856b6ad..0000000 --- a/vendor/github.com/caio/go-tdigest/rng.go +++ /dev/null @@ -1,40 +0,0 @@ -package tdigest - -import ( - "math/rand" -) - -// RNG is an interface that wraps the needed random number -// generator calls that tdigest uses during its runtime -type RNG interface { - Float32() float32 - Intn(int) int -} - -type globalRNG struct{} - -func (r globalRNG) Float32() float32 { - return rand.Float32() -} - -func (r globalRNG) Intn(i int) int { - return rand.Intn(i) -} - -type localRNG struct { - localRand *rand.Rand -} - -func newLocalRNG(seed int64) *localRNG { - return &localRNG{ - localRand: rand.New(rand.NewSource(seed)), - } -} - -func (r *localRNG) Float32() float32 { - return r.localRand.Float32() -} - -func (r *localRNG) Intn(i int) int { - return r.localRand.Intn(i) -} diff --git a/vendor/github.com/caio/go-tdigest/serialization.go b/vendor/github.com/caio/go-tdigest/serialization.go deleted file mode 100644 index b0f2f84..0000000 --- a/vendor/github.com/caio/go-tdigest/serialization.go +++ /dev/null @@ -1,208 +0,0 @@ -package tdigest - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "math" -) - -const smallEncoding int32 = 2 - -var endianess = binary.BigEndian - -// AsBytes serializes the digest into a byte array so it can be -// saved to disk or sent over the wire. -func (t TDigest) AsBytes() ([]byte, error) { - buffer := new(bytes.Buffer) - - err := binary.Write(buffer, endianess, smallEncoding) - - if err != nil { - return nil, err - } - - err = binary.Write(buffer, endianess, t.compression) - - if err != nil { - return nil, err - } - - err = binary.Write(buffer, endianess, int32(t.summary.Len())) - - if err != nil { - return nil, err - } - - var x float64 - t.summary.ForEach(func(mean float64, count uint32) bool { - delta := mean - x - x = mean - err = binary.Write(buffer, endianess, float32(delta)) - - return err == nil - }) - if err != nil { - return nil, err - } - - t.summary.ForEach(func(mean float64, count uint32) bool { - err = encodeUint(buffer, count) - return err == nil - }) - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} - -// FromBytes reads a byte buffer with a serialized digest (from AsBytes) -// and deserializes it. -// -// This function creates a new tdigest instance with the provided options, -// but ignores the compression setting since the correct value comes -// from the buffer. -func FromBytes(buf *bytes.Reader, options ...tdigestOption) (*TDigest, error) { - var encoding int32 - err := binary.Read(buf, endianess, &encoding) - if err != nil { - return nil, err - } - - if encoding != smallEncoding { - return nil, fmt.Errorf("Unsupported encoding version: %d", encoding) - } - - t, err := newWithoutSummary(options...) - - if err != nil { - return nil, err - } - - var compression float64 - err = binary.Read(buf, endianess, &compression) - if err != nil { - return nil, err - } - - t.compression = compression - - var numCentroids int32 - err = binary.Read(buf, endianess, &numCentroids) - if err != nil { - return nil, err - } - - if numCentroids < 0 || numCentroids > 1<<22 { - return nil, errors.New("bad number of centroids in serialization") - } - - t.summary = newSummary(int(numCentroids)) - t.summary.means = t.summary.means[:numCentroids] - t.summary.counts = t.summary.counts[:numCentroids] - - var x float64 - for i := 0; i < int(numCentroids); i++ { - var delta float32 - err = binary.Read(buf, endianess, &delta) - if err != nil { - return nil, err - } - x += float64(delta) - t.summary.means[i] = x - } - - for i := 0; i < int(numCentroids); i++ { - count, err := decodeUint(buf) - if err != nil { - return nil, err - } - t.summary.counts[i] = uint32(count) - t.count += count - } - - return t, nil -} - -// FromBytes deserializes into the supplied TDigest struct, re-using -// and overwriting any existing buffers. -// -// This method reinitializes the digest from the provided buffer -// discarding any previously collected data. Notice that in case -// of errors this may leave the digest in a unusable state. -func (t *TDigest) FromBytes(buf []byte) error { - if len(buf) < 16 { - return errors.New("buffer too small for deserialization") - } - - encoding := int32(endianess.Uint32(buf)) - if encoding != smallEncoding { - return fmt.Errorf("unsupported encoding version: %d", encoding) - } - - compression := math.Float64frombits(endianess.Uint64(buf[4:12])) - numCentroids := int(endianess.Uint32(buf[12:16])) - if numCentroids < 0 || numCentroids > 1<<22 { - return errors.New("bad number of centroids in serialization") - } - - if len(buf) < 16+(4*numCentroids) { - return errors.New("buffer too small for deserialization") - } - - t.count = 0 - t.compression = compression - if t.summary == nil || - cap(t.summary.means) < numCentroids || - cap(t.summary.counts) < numCentroids { - t.summary = newSummary(numCentroids) - } - t.summary.means = t.summary.means[:numCentroids] - t.summary.counts = t.summary.counts[:numCentroids] - - idx := 16 - var x float64 - for i := 0; i < numCentroids; i++ { - delta := math.Float32frombits(endianess.Uint32(buf[idx:])) - idx += 4 - x += float64(delta) - t.summary.means[i] = x - } - - for i := 0; i < numCentroids; i++ { - count, read := binary.Uvarint(buf[idx:]) - if read < 1 { - return errors.New("error decoding varint, this TDigest is now invalid") - } - - idx += read - - t.summary.counts[i] = uint32(count) - t.count += count - } - - if idx != len(buf) { - return errors.New("buffer has unread data") - } - return nil -} - -func encodeUint(buf *bytes.Buffer, n uint32) error { - var b [binary.MaxVarintLen32]byte - - l := binary.PutUvarint(b[:], uint64(n)) - - _, err := buf.Write(b[:l]) - - return err -} - -func decodeUint(buf *bytes.Reader) (uint64, error) { - v, err := binary.ReadUvarint(buf) - if v > 0xffffffff { - return 0, errors.New("Something wrong, this number looks too big") - } - return v, err -} diff --git a/vendor/github.com/caio/go-tdigest/summary.go b/vendor/github.com/caio/go-tdigest/summary.go deleted file mode 100644 index 94ebb18..0000000 --- a/vendor/github.com/caio/go-tdigest/summary.go +++ /dev/null @@ -1,206 +0,0 @@ -package tdigest - -import ( - "fmt" - "math" - "sort" -) - -type summary struct { - means []float64 - counts []uint32 -} - -func newSummary(initialCapacity int) *summary { - s := &summary{ - means: make([]float64, 0, initialCapacity), - counts: make([]uint32, 0, initialCapacity), - } - return s -} - -func (s *summary) Len() int { - return len(s.means) -} - -func (s *summary) Add(key float64, value uint32) error { - if math.IsNaN(key) { - return fmt.Errorf("Key must not be NaN") - } - if value == 0 { - return fmt.Errorf("Count must be >0") - } - - idx := s.findInsertionIndex(key) - - s.means = append(s.means, math.NaN()) - s.counts = append(s.counts, 0) - - copy(s.means[idx+1:], s.means[idx:]) - copy(s.counts[idx+1:], s.counts[idx:]) - - s.means[idx] = key - s.counts[idx] = value - - return nil -} - -// Always insert to the right -func (s *summary) findInsertionIndex(x float64) int { - // Binary search is only worthwhile if we have a lot of keys. - if len(s.means) < 250 { - for i, mean := range s.means { - if mean > x { - return i - } - } - return len(s.means) - } - - return sort.Search(len(s.means), func(i int) bool { - return s.means[i] > x - }) -} - -// This method is the hotspot when calling Add(), which in turn is called by -// Compress() and Merge(). -func (s *summary) HeadSum(idx int) (sum float64) { - return float64(sumUntilIndex(s.counts, idx)) -} - -func (s *summary) Floor(x float64) int { - return s.findIndex(x) - 1 -} - -func (s *summary) findIndex(x float64) int { - // Binary search is only worthwhile if we have a lot of keys. - if len(s.means) < 250 { - for i, mean := range s.means { - if mean >= x { - return i - } - } - return len(s.means) - } - - return sort.Search(len(s.means), func(i int) bool { - return s.means[i] >= x - }) -} - -func (s *summary) Mean(uncheckedIndex int) float64 { - return s.means[uncheckedIndex] -} - -func (s *summary) Count(uncheckedIndex int) uint32 { - return s.counts[uncheckedIndex] -} - -// return the index of the last item which the sum of counts -// of items before it is less than or equal to `sum`. -1 in -// case no centroid satisfies the requirement. -// Since it's cheap, this also returns the `HeadSum` until -// the found index (i.e. cumSum = HeadSum(FloorSum(x))) -func (s *summary) FloorSum(sum float64) (index int, cumSum float64) { - index = -1 - for i, count := range s.counts { - if cumSum <= sum { - index = i - } else { - break - } - cumSum += float64(count) - } - if index != -1 { - cumSum -= float64(s.counts[index]) - } - return index, cumSum -} - -func (s *summary) setAt(index int, mean float64, count uint32) { - s.means[index] = mean - s.counts[index] = count - s.adjustRight(index) - s.adjustLeft(index) -} - -func (s *summary) adjustRight(index int) { - for i := index + 1; i < len(s.means) && s.means[i-1] > s.means[i]; i++ { - s.means[i-1], s.means[i] = s.means[i], s.means[i-1] - s.counts[i-1], s.counts[i] = s.counts[i], s.counts[i-1] - } -} - -func (s *summary) adjustLeft(index int) { - for i := index - 1; i >= 0 && s.means[i] > s.means[i+1]; i-- { - s.means[i], s.means[i+1] = s.means[i+1], s.means[i] - s.counts[i], s.counts[i+1] = s.counts[i+1], s.counts[i] - } -} - -func (s *summary) ForEach(f func(float64, uint32) bool) { - for i, mean := range s.means { - if !f(mean, s.counts[i]) { - break - } - } -} - -func (s *summary) Perm(rng RNG, f func(float64, uint32) bool) { - for _, i := range perm(rng, s.Len()) { - if !f(s.means[i], s.counts[i]) { - break - } - } -} - -func (s *summary) Clone() *summary { - return &summary{ - means: append([]float64{}, s.means...), - counts: append([]uint32{}, s.counts...), - } -} - -// Randomly shuffles summary contents, so they can be added to another summary -// with being pathological. Renders summary invalid. -func (s *summary) shuffle(rng RNG) { - for i := len(s.means) - 1; i > 1; i-- { - s.Swap(i, rng.Intn(i+1)) - } -} - -// for sort.Interface -func (s *summary) Swap(i, j int) { - s.means[i], s.means[j] = s.means[j], s.means[i] - s.counts[i], s.counts[j] = s.counts[j], s.counts[i] -} - -func (s *summary) Less(i, j int) bool { - return s.means[i] < s.means[j] -} - -// A simple loop unroll saves a surprising amount of time. -func sumUntilIndex(s []uint32, idx int) uint64 { - var cumSum uint64 - var i int - for i = idx - 1; i >= 3; i -= 4 { - cumSum += uint64(s[i]) - cumSum += uint64(s[i-1]) - cumSum += uint64(s[i-2]) - cumSum += uint64(s[i-3]) - } - for ; i >= 0; i-- { - cumSum += uint64(s[i]) - } - return cumSum -} - -func perm(rng RNG, n int) []int { - m := make([]int, n) - for i := 1; i < n; i++ { - j := rng.Intn(i + 1) - m[i] = m[j] - m[j] = i - } - return m -} diff --git a/vendor/github.com/caio/go-tdigest/tdigest.go b/vendor/github.com/caio/go-tdigest/tdigest.go deleted file mode 100644 index 9d7e7c0..0000000 --- a/vendor/github.com/caio/go-tdigest/tdigest.go +++ /dev/null @@ -1,446 +0,0 @@ -// Package tdigest provides a highly accurate mergeable data-structure -// for quantile estimation. -// -// Typical T-Digest use cases involve accumulating metrics on several -// distinct nodes of a cluster and then merging them together to get -// a system-wide quantile overview. Things such as: sensory data from -// IoT devices, quantiles over enormous document datasets (think -// ElasticSearch), performance metrics for distributed systems, etc. -// -// After you create (and configure, if desired) the digest: -// digest, err := tdigest.New(tdigest.Compression(100)) -// -// You can then use it for registering measurements: -// digest.Add(number) -// -// Estimating quantiles: -// digest.Quantile(0.99) -// -// And merging with another digest: -// digest.Merge(otherDigest) -package tdigest - -import ( - "fmt" - "math" -) - -// TDigest is a quantile approximation data structure. -type TDigest struct { - summary *summary - compression float64 - count uint64 - rng RNG -} - -// New creates a new digest. -// -// By default the digest is constructed with a configuration that -// should be useful for most use-cases. It comes with compression -// set to 100 and uses the global random number generator (same -// as using math/rand top-level functions). -func New(options ...tdigestOption) (*TDigest, error) { - tdigest, err := newWithoutSummary(options...) - - if err != nil { - return nil, err - } - - tdigest.summary = newSummary(estimateCapacity(tdigest.compression)) - return tdigest, nil -} - -// Creates a tdigest instance without allocating a summary. -func newWithoutSummary(options ...tdigestOption) (*TDigest, error) { - tdigest := &TDigest{ - compression: 100, - count: 0, - rng: globalRNG{}, - } - - for _, option := range options { - err := option(tdigest) - if err != nil { - return nil, err - } - } - - return tdigest, nil -} - -func _quantile(index float64, previousIndex float64, nextIndex float64, previousMean float64, nextMean float64) float64 { - delta := nextIndex - previousIndex - previousWeight := (nextIndex - index) / delta - nextWeight := (index - previousIndex) / delta - return previousMean*previousWeight + nextMean*nextWeight -} - -// Compression returns the TDigest compression. -func (t *TDigest) Compression() float64 { - return t.compression -} - -// Quantile returns the desired percentile estimation. -// -// Values of p must be between 0 and 1 (inclusive), will panic otherwise. -func (t *TDigest) Quantile(q float64) float64 { - if q < 0 || q > 1 { - panic("q must be between 0 and 1 (inclusive)") - } - - if t.summary.Len() == 0 { - return math.NaN() - } else if t.summary.Len() == 1 { - return t.summary.Mean(0) - } - - index := q * float64(t.count-1) - previousMean := math.NaN() - previousIndex := float64(0) - next, total := t.summary.FloorSum(index) - - if next > 0 { - previousMean = t.summary.Mean(next - 1) - previousIndex = total - float64(t.summary.Count(next-1)+1)/2 - } - - for { - nextIndex := total + float64(t.summary.Count(next)-1)/2 - if nextIndex >= index { - if math.IsNaN(previousMean) { - // the index is before the 1st centroid - if nextIndex == previousIndex { - return t.summary.Mean(next) - } - // assume linear growth - nextIndex2 := total + float64(t.summary.Count(next)) + float64(t.summary.Count(next+1)-1)/2 - previousMean = (nextIndex2*t.summary.Mean(next) - nextIndex*t.summary.Mean(next+1)) / (nextIndex2 - nextIndex) - } - // common case: two centroids found, the result in in between - return _quantile(index, previousIndex, nextIndex, previousMean, t.summary.Mean(next)) - } else if next+1 == t.summary.Len() { - // the index is after the last centroid - nextIndex2 := float64(t.count - 1) - nextMean2 := (t.summary.Mean(next)*(nextIndex2-previousIndex) - previousMean*(nextIndex2-nextIndex)) / (nextIndex - previousIndex) - return _quantile(index, nextIndex, nextIndex2, t.summary.Mean(next), nextMean2) - } - total += float64(t.summary.Count(next)) - previousMean = t.summary.Mean(next) - previousIndex = nextIndex - next++ - } - // unreachable -} - -// boundedWeightedAverage computes the weighted average of two -// centroids guaranteeing that the result will be between x1 and x2, -// inclusive. -// -// Refer to https://github.com/caio/go-tdigest/pull/19 for more details -func boundedWeightedAverage(x1 float64, w1 float64, x2 float64, w2 float64) float64 { - if x1 > x2 { - x1, x2, w1, w2 = x2, x1, w2, w1 - } - result := (x1*w1 + x2*w2) / (w1 + w2) - return math.Max(x1, math.Min(result, x2)) -} - -// AddWeighted registers a new sample in the digest. -// -// It's the main entry point for the digest and very likely the only -// method to be used for collecting samples. The count parameter is for -// when you are registering a sample that occurred multiple times - the -// most common value for this is 1. -// -// This will emit an error if `value` is NaN of if `count` is zero. -func (t *TDigest) AddWeighted(value float64, count uint32) (err error) { - - if count == 0 { - return fmt.Errorf("Illegal datapoint ", value, count) - } - - if t.summary.Len() == 0 { - err = t.summary.Add(value, count) - t.count = uint64(count) - return err - } - - begin := t.summary.Floor(value) - if begin == -1 { - begin = 0 - } - - begin, end := t.findNeighbors(begin, value) - - closest := t.chooseMergeCandidate(begin, end, value, count) - - if closest == t.summary.Len() { - err = t.summary.Add(value, count) - if err != nil { - return err - } - } else { - c := float64(t.summary.Count(closest)) - newMean := boundedWeightedAverage(t.summary.Mean(closest), c, value, float64(count)) - t.summary.setAt(closest, newMean, uint32(c)+count) - } - t.count += uint64(count) - - if float64(t.summary.Len()) > 20*t.compression { - err = t.Compress() - } - - return err -} - -// Count returns the total number of samples this digest represents -// -// The result represents how many times Add() was called on a digest -// plus how many samples the digests it has been merged with had. -// This is useful mainly for two scenarios: -// -// - Knowing if there is enough data so you can trust the quantiles -// -// - Knowing if you've registered too many samples already and -// deciding what to do about it. -// -// For the second case one approach would be to create a side empty -// digest and start registering samples on it as well as on the old -// (big) one and then discard the bigger one after a certain criterion -// is reached (say, minimum number of samples or a small relative -// error between new and old digests). -func (t TDigest) Count() uint64 { - return t.count -} - -// Add is an alias for AddWeighted(x,1) -// Read the documentation for AddWeighted for more details. -func (t *TDigest) Add(value float64) error { - return t.AddWeighted(value, 1) -} - -// Compress tries to reduce the number of individual centroids stored -// in the digest. -// -// Compression trades off accuracy for performance and happens -// automatically after a certain amount of distinct samples have been -// stored. -// -// At any point in time you may call Compress on a digest, but you -// may completely ignore this and it will compress itself automatically -// after it grows too much. If you are minimizing network traffic -// it might be a good idea to compress before serializing. -func (t *TDigest) Compress() (err error) { - if t.summary.Len() <= 1 { - return nil - } - - oldTree := t.summary - t.summary = newSummary(estimateCapacity(t.compression)) - t.count = 0 - - oldTree.shuffle(t.rng) - oldTree.ForEach(func(mean float64, count uint32) bool { - err = t.AddWeighted(mean, count) - return err == nil - }) - return err -} - -// Merge joins a given digest into itself. -// -// Merging is useful when you have multiple TDigest instances running -// in separate threads and you want to compute quantiles over all the -// samples. This is particularly important on a scatter-gather/map-reduce -// scenario. -func (t *TDigest) Merge(other *TDigest) (err error) { - if other.summary.Len() == 0 { - return nil - } - - other.summary.Perm(t.rng, func(mean float64, count uint32) bool { - err = t.AddWeighted(mean, count) - return err == nil - }) - return err -} - -// MergeDestructive joins a given digest into itself rendering -// the other digest invalid. -// -// This works as Merge above but its faster. Using this method -// requires caution as it makes 'other' useless - you must make -// sure you discard it without making further uses of it. -func (t *TDigest) MergeDestructive(other *TDigest) (err error) { - if other.summary.Len() == 0 { - return nil - } - - other.summary.shuffle(t.rng) - other.summary.ForEach(func(mean float64, count uint32) bool { - err = t.AddWeighted(mean, count) - return err == nil - }) - return err -} - -// CDF computes the fraction in which all samples are less than -// or equal to the given value. -func (t *TDigest) CDF(value float64) float64 { - if t.summary.Len() == 0 { - return math.NaN() - } else if t.summary.Len() == 1 { - if value < t.summary.Mean(0) { - return 0 - } - return 1 - } - - // We have at least 2 centroids - left := (t.summary.Mean(1) - t.summary.Mean(0)) / 2 - right := left - tot := 0.0 - - for i := 1; i < t.summary.Len()-1; i++ { - prevMean := t.summary.Mean(i - 1) - if value < prevMean+right { - v := (tot + float64(t.summary.Count(i-1))*interpolate(value, prevMean-left, prevMean+right)) / float64(t.Count()) - if v > 0 { - return v - } - return 0 - } - - tot += float64(t.summary.Count(i - 1)) - left = right - right = (t.summary.Mean(i+1) - t.summary.Mean(i)) / 2 - } - - // last centroid, the summary length is at least two - aIdx := t.summary.Len() - 2 - aMean := t.summary.Mean(aIdx) - if value < aMean+right { - aCount := float64(t.summary.Count(aIdx)) - return (tot + aCount*interpolate(value, aMean-left, aMean+right)) / float64(t.Count()) - } - return 1 -} - -// Clone returns a deep copy of a TDigest. -func (t *TDigest) Clone() *TDigest { - return &TDigest{ - summary: t.summary.Clone(), - compression: t.compression, - count: t.count, - rng: t.rng, - } -} - -func interpolate(x, x0, x1 float64) float64 { - return (x - x0) / (x1 - x0) -} - -// ForEachCentroid calls the specified function for each centroid. -// -// Iteration stops when the supplied function returns false, or when all -// centroids have been iterated. -func (t *TDigest) ForEachCentroid(f func(mean float64, count uint32) bool) { - t.summary.ForEach(f) -} - -func (t TDigest) findNeighbors(start int, value float64) (int, int) { - minDistance := math.MaxFloat64 - lastNeighbor := t.summary.Len() - for neighbor := start; neighbor < t.summary.Len(); neighbor++ { - z := math.Abs(t.summary.Mean(neighbor) - value) - if z < minDistance { - start = neighbor - minDistance = z - } else if z > minDistance { - lastNeighbor = neighbor - break - } - } - return start, lastNeighbor -} - -func (t TDigest) chooseMergeCandidate(begin, end int, value float64, count uint32) int { - closest := t.summary.Len() - sum := t.summary.HeadSum(begin) - var n float32 - - for neighbor := begin; neighbor != end; neighbor++ { - c := float64(t.summary.Count(neighbor)) - var q float64 - if t.count == 1 { - q = 0.5 - } else { - q = (sum + (c-1)/2) / float64(t.count-1) - } - k := 4 * float64(t.count) * q * (1 - q) / t.compression - - if c+float64(count) <= k { - n++ - if t.rng.Float32() < 1/n { - closest = neighbor - } - } - sum += c - } - return closest -} - -// TrimmedMean returns the mean of the distribution between the two -// percentiles p1 and p2. -// -// Values of p1 and p2 must be beetween 0 and 1 (inclusive) and p1 -// must be less than p2. Will panic otherwise. -func (t *TDigest) TrimmedMean(p1, p2 float64) float64 { - if p1 < 0 || p1 > 1 { - panic("p1 must be between 0 and 1 (inclusive)") - } - if p2 < 0 || p2 > 1 { - panic("p2 must be between 0 and 1 (inclusive)") - } - if p1 >= p2 { - panic("p1 must be lower than p2") - } - - minCount := p1 * float64(t.count) - maxCount := p2 * float64(t.count) - - var trimmedSum, trimmedCount, currCount float64 - for i, mean := range t.summary.means { - count := float64(t.summary.counts[i]) - - nextCount := currCount + count - if nextCount <= minCount { - currCount = nextCount - continue - } - - if currCount < minCount { - count = nextCount - minCount - } - if nextCount > maxCount { - count -= nextCount - maxCount - } - - trimmedSum += count * mean - trimmedCount += count - - if nextCount >= maxCount { - break - } - currCount = nextCount - } - - if trimmedCount == 0 { - return 0 - } - return trimmedSum / trimmedCount -} - -func estimateCapacity(compression float64) int { - return int(compression) * 10 -} diff --git a/vendor/github.com/getsentry/sentry-go/.craft.yml b/vendor/github.com/getsentry/sentry-go/.craft.yml new file mode 100644 index 0000000..6d23f0b --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/.craft.yml @@ -0,0 +1,12 @@ +github: + owner: getsentry + repo: sentry-go +preReleaseCommand: bash scripts/craft-pre-release.sh +changelogPolicy: simple +targets: + - name: github + tagPrefix: v + - name: registry + type: sdk + config: + canonical: "github:getsentry/sentry-go" diff --git a/vendor/github.com/getsentry/sentry-go/.gitignore b/vendor/github.com/getsentry/sentry-go/.gitignore new file mode 100644 index 0000000..29bfd6f --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/.gitignore @@ -0,0 +1,6 @@ +coverage.txt + +# Just my personal way of tracking stuff — Kamil +FIXME.md +TODO.md +!NOTES.md \ No newline at end of file diff --git a/vendor/github.com/getsentry/sentry-go/.golangci.yml b/vendor/github.com/getsentry/sentry-go/.golangci.yml new file mode 100644 index 0000000..57a83cc --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/.golangci.yml @@ -0,0 +1,13 @@ +linters: + enable-all: true + disable: + - megacheck + - stylecheck +run: + skip-dirs: + - echo + - example/echo +issues: + exclude: + - "not declared by package utf8" + - "unicode/utf8/utf8.go" diff --git a/vendor/github.com/getsentry/sentry-go/.travis.yml b/vendor/github.com/getsentry/sentry-go/.travis.yml new file mode 100644 index 0000000..e0d415a --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/.travis.yml @@ -0,0 +1,29 @@ +sudo: false + +language: go +go: + - 1.11.x + - 1.12.x + - 1.13.x + +env: + - GO111MODULE=on + +before_install: + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0 + +script: + - golangci-lint run + - go build + - go test + - go test -race + +notifications: + webhooks: + urls: + - https://zeus.ci/hooks/befe9810-9285-11e9-b01a-0a580a281808/public/provider/travis/webhook + on_success: always + on_failure: always + on_start: always + on_cancel: always + on_error: always diff --git a/vendor/github.com/getsentry/sentry-go/CHANGELOG.md b/vendor/github.com/getsentry/sentry-go/CHANGELOG.md new file mode 100644 index 0000000..b234bee --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/CHANGELOG.md @@ -0,0 +1,125 @@ +# Changelog + +## Unreleased + +- "I am running away from my responsibilities. And it feels good." – Michael Scott, Season 4, "Money" + +## v0.3.0 + +- feat: Retry event marshalling without contextual data if the first pass fails +- fix: Include `url.Parse` error in `DsnParseError` +- fix: Make more `Scope` methods safe for concurrency +- fix: Synchronize concurrent access to `Hub.client` +- ref: Remove mutex from `Scope` exported API +- ref: Remove mutex from `Hub` exported API +- ref: Compile regexps for `filterFrames` only once +- ref: Change `SampleRate` type to `float64` +- doc: `Scope.Clear` not safe for concurrent use +- ci: Test sentry-go with `go1.13`, drop `go1.10` + +_NOTE:_ +This version removes some of the internal APIs that landed publicly (namely `Hub/Scope` mutex structs) and may require (but shouldn't) some changes to your code. +It's not done through major version update, as we are still in `0.x` stage. + +## v0.2.1 + +- fix: Run `Contextify` integration on `Threads` as well + +## v0.2.0 + +- feat: Add `SetTransaction()` method on the `Scope` +- feat: `fasthttp` framework support with `sentryfasthttp` package +- fix: Add `RWMutex` locks to internal `Hub` and `Scope` changes + +## v0.1.3 + +- feat: Move frames context reading into `contextifyFramesIntegration` (#28) + +_NOTE:_ +In case of any performance isues due to source contexts IO, you can let us know and turn off the integration in the meantime with: + +```go +sentry.Init(sentry.ClientOptions{ + Integrations: func(integrations []sentry.Integration) []sentry.Integration { + var filteredIntegrations []sentry.Integration + for _, integration := range integrations { + if integration.Name() == "ContextifyFrames" { + continue + } + filteredIntegrations = append(filteredIntegrations, integration) + } + return filteredIntegrations + }, +}) +``` + +## v0.1.2 + +- feat: Better source code location resolution and more useful inapp frames (#26) +- feat: Use `noopTransport` when no `Dsn` provided (#27) +- ref: Allow empty `Dsn` instead of returning an error (#22) +- fix: Use `NewScope` instead of literal struct inside a `scope.Clear` call (#24) +- fix: Add to `WaitGroup` before the request is put inside a buffer (#25) + +## v0.1.1 + +- fix: Check for initialized `Client` in `AddBreadcrumbs` (#20) +- build: Bump version when releasing with Craft (#19) + +## v0.1.0 + +- First stable release! \o/ + +## v0.0.1-beta.5 + +- feat: **[breaking]** Add `NewHTTPTransport` and `NewHTTPSyncTransport` which accepts all transport options +- feat: New `HTTPSyncTransport` that blocks after each call +- feat: New `Echo` integration +- ref: **[breaking]** Remove `BufferSize` option from `ClientOptions` and move it to `HTTPTransport` instead +- ref: Export default `HTTPTransport` +- ref: Export `net/http` integration handler +- ref: Set `Request` instantly in the package handlers, not in `recoverWithSentry` so it can be accessed later on +- ci: Add craft config + +## v0.0.1-beta.4 + +- feat: `IgnoreErrors` client option and corresponding integration +- ref: Reworked `net/http` integration, wrote better example and complete readme +- ref: Reworked `Gin` integration, wrote better example and complete readme +- ref: Reworked `Iris` integration, wrote better example and complete readme +- ref: Reworked `Negroni` integration, wrote better example and complete readme +- ref: Reworked `Martini` integration, wrote better example and complete readme +- ref: Remove `Handle()` from frameworks handlers and return it directly from New + +## v0.0.1-beta.3 + +- feat: `Iris` framework support with `sentryiris` package +- feat: `Gin` framework support with `sentrygin` package +- feat: `Martini` framework support with `sentrymartini` package +- feat: `Negroni` framework support with `sentrynegroni` package +- feat: Add `Hub.Clone()` for easier frameworks integration +- feat: Return `EventID` from `Recovery` methods +- feat: Add `NewScope` and `NewEvent` functions and use them in the whole codebase +- feat: Add `AddEventProcessor` to the `Client` +- fix: Operate on requests body copy instead of the original +- ref: Try to read source files from the root directory, based on the filename as well, to make it work on AWS Lambda +- ref: Remove `gocertifi` dependence and document how to provide your own certificates +- ref: **[breaking]** Remove `Decorate` and `DecorateFunc` methods in favor of `sentryhttp` package +- ref: **[breaking]** Allow for integrations to live on the client, by passing client instance in `SetupOnce` method +- ref: **[breaking]** Remove `GetIntegration` from the `Hub` +- ref: **[breaking]** Remove `GlobalEventProcessors` getter from the public API + +## v0.0.1-beta.2 + +- feat: Add `AttachStacktrace` client option to include stacktrace for messages +- feat: Add `BufferSize` client option to configure transport buffer size +- feat: Add `SetRequest` method on a `Scope` to control `Request` context data +- feat: Add `FromHTTPRequest` for `Request` type for easier extraction +- ref: Extract `Request` information more accurately +- fix: Attach `ServerName`, `Release`, `Dist`, `Environment` options to the event +- fix: Don't log events dropped due to full transport buffer as sent +- fix: Don't panic and create an appropriate event when called `CaptureException` or `Recover` with `nil` value + +## v0.0.1-beta + +- Initial release diff --git a/vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md b/vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md new file mode 100644 index 0000000..077c310 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md @@ -0,0 +1,41 @@ +## Testing + +```bash +$ go test +``` + +### Watch mode + +Use: https://github.com/cespare/reflex + +```bash +$ reflex -g '*.go' -d "none" -- sh -c 'printf "\n"; go test' +``` + +### With data race detection + +```bash +$ go test -race +``` + +### Coverage +```bash +$ go test -race -coverprofile=coverage.txt -covermode=atomic && go tool cover -html coverage.txt +``` + +## Linting + +```bash +$ golangci-lint run +``` + +## Release + +1. Update changelog with new version in `vX.X.X` format title and list of changes +2. Commit with `misc: vX.X.X changelog` commit message and push to `master` +3. Let `craft` do the rest + +```bash +$ craft prepare X.X.X +$ craft publish X.X.X --skip-status-check +``` \ No newline at end of file diff --git a/vendor/github.com/getsentry/sentry-go/LICENSE b/vendor/github.com/getsentry/sentry-go/LICENSE new file mode 100644 index 0000000..3e66f28 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/getsentry/sentry-go/MIGRATION.md b/vendor/github.com/getsentry/sentry-go/MIGRATION.md new file mode 100644 index 0000000..08b3607 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/MIGRATION.md @@ -0,0 +1,392 @@ +# `raven-go` to `sentry-go` Migration Guide + +## Installation + +raven-go + +```go +go get github.com/getsentry/raven-go +``` + +sentry-go + +```go +go get github.com/getsentry/sentry-go@v0.0.1 +``` + +## Configuration + +raven-go + +```go +import "github.com/getsentry/raven-go" + +func main() { + raven.SetDSN("https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1") +} +``` + +sentry-go + +```go +import ( + "fmt" + "github.com/getsentry/sentry-go" +) + +func main() { + err := sentry.Init(sentry.ClientOptions{ + Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1", + }) + + if err != nil { + fmt.Printf("Sentry initialization failed: %v\n", err) + } +} +``` + +raven-go + +```go +SetDSN() +SetDefaultLoggerName() +SetDebug() +SetEnvironment() +SetRelease() +SetSampleRate() +SetIgnoreErrors() +SetIncludePaths() +``` + +sentry-go + +```go +sentry.Init(sentry.ClientOptions{ + Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1", + DebugWriter: os.Stderr, + Debug: true, + Environment: "environment", + Release: "release", + SampleRate: 0.5, + // IgnoreErrors: TBD, + // IncludePaths: TBD +}) +``` + +Available options: see [Configuration](https://docs.sentry.io/platforms/go/config/) section. + +### Providing SSL Certificates + +By default, TLS uses the host's root CA set. If you don't have `ca-certificates` (which should be your go-to way of fixing the issue of missing ceritificates) and want to use `gocertifi` instead, you can provide pre-loaded cert files as one of the options to the `sentry.Init` call: + +```go +package main + +import ( + "log" + + "github.com/certifi/gocertifi" + "github.com/getsentry/sentry-go" +) + +sentryClientOptions := sentry.ClientOptions{ + Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1", +} + +rootCAs, err := gocertifi.CACerts() +if err != nil { + log.Println("Coudnt load CA Certificates: %v\n", err) +} else { + sentryClientOptions.CaCerts = rootCAs +} + +sentry.Init(sentryClientOptions) +``` + +## Usage + +### Capturing Errors + +raven-go + +```go +f, err := os.Open("filename.ext") +if err != nil { + raven.CaptureError(err, nil) +} +``` + +sentry-go + +```go +f, err := os.Open("filename.ext") +if err != nil { + sentry.CaptureException(err) +} +``` + +### Capturing Panics + +raven-go + +```go +raven.CapturePanic(func() { + // do all of the scary things here +}, nil) +``` + +sentry-go + +```go +func() { + defer sentry.Recover() + // do all of the scary things here +}() +``` + +### Capturing Messages + +raven-go + +```go +raven.CaptureMessage("Something bad happened and I would like to know about that") +``` + +sentry-go + +```go +sentry.CaptureMessage("Something bad happened and I would like to know about that") +``` + +### Capturing Events + +raven-go + +```go +packet := &raven.Packet{ + Message: "Hand-crafted event", + Extra: &raven.Extra{ + "runtime.Version": runtime.Version(), + "runtime.NumCPU": runtime.NumCPU(), + }, +} +raven.Capture(packet) +``` + +sentry-go + +```go +event := &sentry.NewEvent() +event.Message = "Hand-crafted event" +event.Extra["runtime.Version"] = runtime.Version() +event.Extra["runtime.NumCPU"] = runtime.NumCPU() + +sentry.CaptureEvent(event) +``` + +### Additional Data + +See Context section. + +### Event Sampling + +raven-go + +```go +raven.SetSampleRate(0.25) +``` + +sentry-go + +```go +sentry.Init(sentry.ClientOptions{ + SampleRate: 0.25, +}) +``` + +### Awaiting the response (not recommended) + +```go +raven.CaptureMessageAndWait("Something bad happened and I would like to know about that") +``` + +sentry-go + +```go +sentry.CaptureMessage("Something bad happened and I would like to know about that") + +if sentry.Flush(time.Second * 2) { + // event delivered +} else { + // timeout reached +} +``` + +## Context + +### Per-event + +raven-go + +```go +raven.CaptureError(err, map[string]string{"browser": "Firefox"}, &raven.Http{ + Method: "GET", + URL: "https://example.com/raven-go" +}) +``` + +sentry-go + +```go +sentry.WithScope(func(scope *sentry.Scope) { + scope.SetTag("browser", "Firefox") + scope.SetContext("Request", map[string]string{ + "Method": "GET", + "URL": "https://example.com/raven-go", + }) + sentry.CaptureException(err) +}) +``` + +### Globally + +#### SetHttpContext + +raven-go + +```go +raven.SetHttpContext(&raven.Http{ + Method: "GET", + URL: "https://example.com/raven-go", +}) +``` + +sentry-go + +```go +sentry.ConfigureScope(func(scope *sentry.Scope) { + scope.SetContext("Request", map[string]string{ + "Method": "GET", + "URL": "https://example.com/raven-go", + }) +}) +``` + +#### SetTagsContext + +raven-go + +```go +t := map[string]string{"day": "Friday", "sport": "Weightlifting"} +raven.SetTagsContext(map[string]string{"day": "Friday", "sport": "Weightlifting"}) +``` + +sentry-go + +```go +sentry.ConfigureScope(func(scope *sentry.Scope) { + scope.SetTags(map[string]string{"day": "Friday", "sport": "Weightlifting"}) +}) +``` + +#### SetUserContext + +raven-go + +```go +raven.SetUserContext(&raven.User{ + ID: "1337", + Username: "kamilogorek", + Email: "kamil@sentry.io", + IP: "127.0.0.1", +}) +``` + +sentry-go + +```go +sentry.ConfigureScope(func(scope *sentry.Scope) { + scope.SetUser(sentry.User{ + ID: "1337", + Username: "kamilogorek", + Email: "kamil@sentry.io", + IPAddress: "127.0.0.1", + }) +}) +``` + +#### ClearContext + +raven-go + +```go +raven.ClearContext() +``` + +sentry-go + +```go +sentry.ConfigureScope(func(scope *sentry.Scope) { + scope.Clear() +}) +``` + +#### WrapWithExtra + +raven-go + +```go +path := "filename.ext" +f, err := os.Open(path) +if err != nil { + err = raven.WrapWithExtra(err, map[string]string{"path": path, "cwd": os.Getwd()} + raven.CaptureError(err, nil) +} +``` + +sentry-go + +```go +// use `sentry.WithScope`, see "Context / Per-event Section" +path := "filename.ext" +f, err := os.Open(path) +if err != nil { + sentry.WithScope(func(scope *sentry.Scope) { + sentry.SetExtras(map[string]interface{}{"path": path, "cwd": os.Getwd()) + sentry.CaptureException(err) + }) +} +``` + +## Integrations + +### net/http + +raven-go + +```go +mux := http.NewServeMux +http.Handle("/", raven.Recoverer(mux)) + +// or + +func root(w http.ResponseWriter, r *http.Request) {} +http.HandleFunc("/", raven.RecoveryHandler(root)) +``` + +sentry-go + +```go +sentryHandler := sentryhttp.New(sentryhttp.Options{ + Repanic: false, + WaitForDelivery: true, +}) + +mux := http.NewServeMux +http.Handle("/", sentryHandler.Handle(mux)) + +// or + +func root(w http.ResponseWriter, r *http.Request) {} +http.HandleFunc("/", sentryHandler.HandleFunc(root)) +``` diff --git a/vendor/github.com/getsentry/sentry-go/README.md b/vendor/github.com/getsentry/sentry-go/README.md new file mode 100644 index 0000000..8dca376 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/README.md @@ -0,0 +1,108 @@ +

+ + + +
+

+ +# Official Sentry SDK for Go + +[![Build Status](https://travis-ci.com/getsentry/sentry-go.svg?branch=master)](https://travis-ci.com/getsentry/sentry-go) +[![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/sentry-go)](https://goreportcard.com/report/github.com/getsentry/sentry-go) + +`sentry-go` provides a Sentry client implementation for the Go programming language. This is the next line of the Go SDK for [Sentry](https://sentry.io/), intended to replace the `raven-go` package. + +> Looking for the old `raven-go` SDK documentation? See the Legacy client section [here](https://docs.sentry.io/clients/go/). +> If you want to start using sentry-go instead, check out the [migration guide](https://docs.sentry.io/platforms/go/migration/). + +## Requirements + +We verify this package against N-2 recent versions of Go compiler. As of September 2019, those versions are: + +* 1.11 +* 1.12 +* 1.13 + +## Installation + +`sentry-go` can be installed like any other Go library through `go get`: + +```bash +$ go get github.com/getsentry/sentry-go +``` + +Or, if you are already using Go Modules, specify a version number as well: + +```bash +$ go get github.com/getsentry/sentry-go@v0.1.0 +``` + +## Configuration + +To use `sentry-go`, you’ll need to import the `sentry-go` package and initialize it with the client options that will include your DSN. If you specify the `SENTRY_DSN` environment variable, you can omit this value from options and it will be picked up automatically for you. The release and environment can also be specified in the environment variables `SENTRY_RELEASE` and `SENTRY_ENVIRONMENT` respectively. + +More on this in [Configuration](https://docs.sentry.io/platforms/go/config/) section. + +## Usage + +By default, Sentry Go SDK uses asynchronous transport, which in the code example below requires an explicit awaiting for event delivery to be finished using `sentry.Flush` method. It is necessary, because otherwise the program would not wait for the async HTTP calls to return a response, and exit the process immediately when it reached the end of the `main` function. It would not be required inside a running goroutine or if you would use `HTTPSyncTransport`, which you can read about in `Transports` section. + +```go +package main + +import ( + "fmt" + "os" + "time" + + "github.com/getsentry/sentry-go" +) + +func main() { + err := sentry.Init(sentry.ClientOptions{ + Dsn: "___DSN___", + }) + + if err != nil { + fmt.Printf("Sentry initialization failed: %v\n", err) + } + + f, err := os.Open("filename.ext") + if err != nil { + sentry.CaptureException(err) + sentry.Flush(time.Second * 5) + } +} +``` + +For more detailed information about how to get the most out of `sentry-go` there is additional documentation available: + +- [Configuration](https://docs.sentry.io/platforms/go/config) +- [Error Reporting](https://docs.sentry.io/error-reporting/quickstart?platform=go) +- [Enriching Error Data](https://docs.sentry.io/enriching-error-data/context?platform=go) +- [Transports](https://docs.sentry.io/platforms/go/transports) +- [Integrations](https://docs.sentry.io/platforms/go/integrations) + - [net/http](https://docs.sentry.io/platforms/go/http) + - [echo](https://docs.sentry.io/platforms/go/echo) + - [fasthttp](https://docs.sentry.io/platforms/go/fasthttp) + - [gin](https://docs.sentry.io/platforms/go/gin) + - [iris](https://docs.sentry.io/platforms/go/iris) + - [martini](https://docs.sentry.io/platforms/go/martini) + - [negroni](https://docs.sentry.io/platforms/go/negroni) + +## Resources: + +- [Bug Tracker](https://github.com/getsentry/sentry-go/issues) +- [GitHub Project](https://github.com/getsentry/sentry-go) +- [Godocs](https://godoc.org/github.com/getsentry/sentry-go) +- [@getsentry](https://twitter.com/getsentry) on Twitter for updates + +## License + +Licensed under the BSD license, see `LICENSE` + +## Community + +Want to join our Sentry's `community-golang` channel, get involved and help us improve the SDK? + +Do not hesistate to shoot me up an email at [kamil@sentry.io](mailto:kamil@sentry.io) for Slack invite! diff --git a/vendor/github.com/getsentry/sentry-go/client.go b/vendor/github.com/getsentry/sentry-go/client.go new file mode 100644 index 0000000..2465bb3 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/client.go @@ -0,0 +1,429 @@ +package sentry + +import ( + "context" + "crypto/x509" + "fmt" + "io" + "io/ioutil" + "log" + "math/rand" + "net/http" + "os" + "reflect" + "sort" + "time" +) + +// Logger is an instance of log.Logger that is use to provide debug information about running Sentry Client +// can be enabled by either using `Logger.SetOutput` directly or with `Debug` client option +var Logger = log.New(ioutil.Discard, "[Sentry] ", log.LstdFlags) // nolint: gochecknoglobals + +type EventProcessor func(event *Event, hint *EventHint) *Event + +type EventModifier interface { + ApplyToEvent(event *Event, hint *EventHint) *Event +} + +var globalEventProcessors []EventProcessor // nolint: gochecknoglobals + +func AddGlobalEventProcessor(processor EventProcessor) { + globalEventProcessors = append(globalEventProcessors, processor) +} + +// Integration allows for registering a functions that modify or discard captured events. +type Integration interface { + Name() string + SetupOnce(client *Client) +} + +// ClientOptions that configures a SDK Client +type ClientOptions struct { + // The DSN to use. If the DSN is not set, the client is effectively disabled. + Dsn string + // In debug mode, the debug information is printed to stdout to help you understand what + // sentry is doing. + Debug bool + // Configures whether SDK should generate and attach stacktraces to pure capture message calls. + AttachStacktrace bool + // The sample rate for event submission (0.0 - 1.0, defaults to 1.0). + SampleRate float64 + // List of regexp strings that will be used to match against event's message + // and if applicable, caught errors type and value. + // If the match is found, then a whole event will be dropped. + IgnoreErrors []string + // Before send callback. + BeforeSend func(event *Event, hint *EventHint) *Event + // Before breadcrumb add callback. + BeforeBreadcrumb func(breadcrumb *Breadcrumb, hint *BreadcrumbHint) *Breadcrumb + // Integrations to be installed on the current Client, receives default integrations + Integrations func([]Integration) []Integration + // io.Writer implementation that should be used with the `Debug` mode + DebugWriter io.Writer + // The transport to use. + // This is an instance of a struct implementing `Transport` interface. + // Defaults to `httpTransport` from `transport.go` + Transport Transport + // The server name to be reported. + ServerName string + // The release to be sent with events. + Release string + // The dist to be sent with events. + Dist string + // The environment to be sent with events. + Environment string + // Maximum number of breadcrumbs. + MaxBreadcrumbs int + // An optional pointer to `http.Transport` that will be used with a default HTTPTransport. + HTTPTransport *http.Transport + // An optional HTTP proxy to use. + // This will default to the `http_proxy` environment variable. + // or `https_proxy` if that one exists. + HTTPProxy string + // An optional HTTPS proxy to use. + // This will default to the `HTTPS_PROXY` environment variable + // or `http_proxy` if that one exists. + HTTPSProxy string + // An optional CaCerts to use. + // Defaults to `gocertifi.CACerts()`. + CaCerts *x509.CertPool +} + +// Client is the underlying processor that's used by the main API and `Hub` instances. +type Client struct { + options ClientOptions + dsn *Dsn + eventProcessors []EventProcessor + integrations []Integration + Transport Transport +} + +// NewClient creates and returns an instance of `Client` configured using `ClientOptions`. +func NewClient(options ClientOptions) (*Client, error) { + if options.Debug { + debugWriter := options.DebugWriter + if debugWriter == nil { + debugWriter = os.Stdout + } + Logger.SetOutput(debugWriter) + } + + if options.Dsn == "" { + options.Dsn = os.Getenv("SENTRY_DSN") + } + + if options.Release == "" { + options.Release = os.Getenv("SENTRY_RELEASE") + } + + if options.Environment == "" { + options.Environment = os.Getenv("SENTRY_ENVIRONMENT") + } + + var dsn *Dsn + if options.Dsn != "" { + var err error + dsn, err = NewDsn(options.Dsn) + if err != nil { + return nil, err + } + } + + client := Client{ + options: options, + dsn: dsn, + } + + client.setupTransport() + client.setupIntegrations() + + return &client, nil +} + +func (client *Client) setupTransport() { + transport := client.options.Transport + + if transport == nil { + if client.options.Dsn == "" { + transport = new(noopTransport) + } else { + transport = NewHTTPTransport() + } + } + + transport.Configure(client.options) + client.Transport = transport +} + +func (client *Client) setupIntegrations() { + integrations := []Integration{ + new(contextifyFramesIntegration), + new(environmentIntegration), + new(modulesIntegration), + new(ignoreErrorsIntegration), + } + + if client.options.Integrations != nil { + integrations = client.options.Integrations(integrations) + } + + for _, integration := range integrations { + if client.integrationAlreadyInstalled(integration.Name()) { + Logger.Printf("Integration %s is already installed\n", integration.Name()) + continue + } + client.integrations = append(client.integrations, integration) + integration.SetupOnce(client) + Logger.Printf("Integration installed: %s\n", integration.Name()) + } +} + +// AddEventProcessor adds an event processor to the client. +func (client *Client) AddEventProcessor(processor EventProcessor) { + client.eventProcessors = append(client.eventProcessors, processor) +} + +// Options return `ClientOptions` for the current `Client`. +func (client Client) Options() ClientOptions { + return client.options +} + +// CaptureMessage captures an arbitrary message. +func (client *Client) CaptureMessage(message string, hint *EventHint, scope EventModifier) *EventID { + event := client.eventFromMessage(message, LevelInfo) + return client.CaptureEvent(event, hint, scope) +} + +// CaptureException captures an error. +func (client *Client) CaptureException(exception error, hint *EventHint, scope EventModifier) *EventID { + event := client.eventFromException(exception, LevelError) + return client.CaptureEvent(event, hint, scope) +} + +// CaptureEvent captures an event on the currently active client if any. +// +// The event must already be assembled. Typically code would instead use +// the utility methods like `CaptureException`. The return value is the +// event ID. In case Sentry is disabled or event was dropped, the return value will be nil. +func (client *Client) CaptureEvent(event *Event, hint *EventHint, scope EventModifier) *EventID { + return client.processEvent(event, hint, scope) +} + +// Recover captures a panic. +// Returns `EventID` if successfully, or `nil` if there's no error to recover from. +func (client *Client) Recover(err interface{}, hint *EventHint, scope EventModifier) *EventID { + if err == nil { + err = recover() + } + + if err != nil { + if err, ok := err.(error); ok { + event := client.eventFromException(err, LevelFatal) + return client.CaptureEvent(event, hint, scope) + } + + if err, ok := err.(string); ok { + event := client.eventFromMessage(err, LevelFatal) + return client.CaptureEvent(event, hint, scope) + } + } + + return nil +} + +// Recover captures a panic and passes relevant context object. +// Returns `EventID` if successfully, or `nil` if there's no error to recover from. +func (client *Client) RecoverWithContext( + ctx context.Context, + err interface{}, + hint *EventHint, + scope EventModifier, +) *EventID { + if err == nil { + err = recover() + } + + if err != nil { + if hint.Context == nil && ctx != nil { + hint.Context = ctx + } + + if err, ok := err.(error); ok { + event := client.eventFromException(err, LevelFatal) + return client.CaptureEvent(event, hint, scope) + } + + if err, ok := err.(string); ok { + event := client.eventFromMessage(err, LevelFatal) + return client.CaptureEvent(event, hint, scope) + } + } + + return nil +} + +// Flush notifies when all the buffered events have been sent by returning `true` +// or `false` if timeout was reached. It calls `Flush` method of the configured `Transport`. +func (client *Client) Flush(timeout time.Duration) bool { + return client.Transport.Flush(timeout) +} + +func (client *Client) eventFromMessage(message string, level Level) *Event { + event := NewEvent() + event.Level = level + event.Message = message + + if client.Options().AttachStacktrace { + event.Threads = []Thread{{ + Stacktrace: NewStacktrace(), + Crashed: false, + Current: true, + }} + } + + return event +} + +func (client *Client) eventFromException(exception error, level Level) *Event { + if exception == nil { + event := NewEvent() + event.Level = level + event.Message = fmt.Sprintf("Called %s with nil value", callerFunctionName()) + return event + } + + stacktrace := ExtractStacktrace(exception) + + if stacktrace == nil { + stacktrace = NewStacktrace() + } + + event := NewEvent() + event.Level = level + event.Exception = []Exception{{ + Value: exception.Error(), + Type: reflect.TypeOf(exception).String(), + Stacktrace: stacktrace, + }} + return event +} + +func (client *Client) processEvent(event *Event, hint *EventHint, scope EventModifier) *EventID { + options := client.Options() + + // TODO: Reconsider if its worth going away from default implementation + // of other SDKs. In Go zero value (default) for float32 is 0.0, + // which means that if someone uses ClientOptions{} struct directly + // and we would not check for 0 here, we'd skip all events by default + if options.SampleRate != 0.0 { + randomFloat := rand.New(rand.NewSource(time.Now().UnixNano())).Float64() + if randomFloat > options.SampleRate { + Logger.Println("Event dropped due to SampleRate hit.") + return nil + } + } + + if event = client.prepareEvent(event, hint, scope); event == nil { + return nil + } + + if options.BeforeSend != nil { + h := &EventHint{} + if hint != nil { + h = hint + } + if event = options.BeforeSend(event, h); event == nil { + Logger.Println("Event dropped due to BeforeSend callback.") + return nil + } + } + + client.Transport.SendEvent(event) + + return &event.EventID +} + +func (client *Client) prepareEvent(event *Event, hint *EventHint, scope EventModifier) *Event { + if event.EventID == "" { + event.EventID = EventID(uuid()) + } + + if event.Timestamp == 0 { + event.Timestamp = time.Now().Unix() + } + + if event.Level == "" { + event.Level = LevelInfo + } + + if event.ServerName == "" { + if client.Options().ServerName != "" { + event.ServerName = client.Options().ServerName + } else if hostname, err := os.Hostname(); err == nil { + event.ServerName = hostname + } + } + + if event.Release == "" && client.Options().Release != "" { + event.Release = client.Options().Release + } + + if event.Dist == "" && client.Options().Dist != "" { + event.Dist = client.Options().Dist + } + + if event.Environment == "" && client.Options().Environment != "" { + event.Environment = client.Options().Environment + } + + event.Platform = "go" + event.Sdk = SdkInfo{ + Name: "sentry.go", + Version: Version, + Integrations: client.listIntegrations(), + Packages: []SdkPackage{{ + Name: "sentry-go", + Version: Version, + }}, + } + + event = scope.ApplyToEvent(event, hint) + + for _, processor := range client.eventProcessors { + id := event.EventID + event = processor(event, hint) + if event == nil { + Logger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id) + return nil + } + } + + for _, processor := range globalEventProcessors { + id := event.EventID + event = processor(event, hint) + if event == nil { + Logger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id) + return nil + } + } + + return event +} + +func (client Client) listIntegrations() []string { + integrations := make([]string, 0, len(client.integrations)) + for _, integration := range client.integrations { + integrations = append(integrations, integration.Name()) + } + sort.Strings(integrations) + return integrations +} + +func (client Client) integrationAlreadyInstalled(name string) bool { + for _, integration := range client.integrations { + if integration.Name() == name { + return true + } + } + return false +} diff --git a/vendor/github.com/getsentry/sentry-go/dsn.go b/vendor/github.com/getsentry/sentry-go/dsn.go new file mode 100644 index 0000000..cf7632e --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/dsn.go @@ -0,0 +1,185 @@ +package sentry + +import ( + "encoding/json" + "fmt" + "net/url" + "strconv" + "strings" + "time" +) + +type scheme string + +const ( + schemeHTTP scheme = "http" + schemeHTTPS scheme = "https" +) + +func (scheme scheme) defaultPort() int { + switch scheme { + case schemeHTTPS: + return 443 + case schemeHTTP: + return 80 + default: + return 80 + } +} + +type DsnParseError struct { + Message string +} + +func (e DsnParseError) Error() string { + return "[Sentry] DsnParseError: " + e.Message +} + +// Dsn is used as the remote address source to client transport. +type Dsn struct { + scheme scheme + publicKey string + secretKey string + host string + port int + path string + projectID int +} + +// NewDsn creates an instance od `Dsn` by parsing provided url in a `string` format. +// If Dsn is not set the client is effectively disabled. +func NewDsn(rawURL string) (*Dsn, error) { + // Parse + parsedURL, err := url.Parse(rawURL) + if err != nil { + return nil, &DsnParseError{fmt.Sprintf("invalid url: %v", err)} + } + + // Scheme + var scheme scheme + switch parsedURL.Scheme { + case "http": + scheme = schemeHTTP + case "https": + scheme = schemeHTTPS + default: + return nil, &DsnParseError{"invalid scheme"} + } + + // PublicKey + publicKey := parsedURL.User.Username() + if publicKey == "" { + return nil, &DsnParseError{"empty username"} + } + + // SecretKey + var secretKey string + if parsedSecretKey, ok := parsedURL.User.Password(); ok { + secretKey = parsedSecretKey + } + + // Host + host := parsedURL.Hostname() + if host == "" { + return nil, &DsnParseError{"empty host"} + } + + // Port + var port int + if parsedURL.Port() != "" { + parsedPort, err := strconv.Atoi(parsedURL.Port()) + if err != nil { + return nil, &DsnParseError{"invalid port"} + } + port = parsedPort + } else { + port = scheme.defaultPort() + } + + // ProjectID + if len(parsedURL.Path) == 0 || parsedURL.Path == "/" { + return nil, &DsnParseError{"empty project id"} + } + pathSegments := strings.Split(parsedURL.Path[1:], "/") + projectID, err := strconv.Atoi(pathSegments[len(pathSegments)-1]) + if err != nil { + return nil, &DsnParseError{"invalid project id"} + } + + // Path + var path string + if len(pathSegments) > 1 { + path = "/" + strings.Join(pathSegments[0:len(pathSegments)-1], "/") + } + + return &Dsn{ + scheme: scheme, + publicKey: publicKey, + secretKey: secretKey, + host: host, + port: port, + path: path, + projectID: projectID, + }, nil +} + +// String formats Dsn struct into a valid string url +func (dsn Dsn) String() string { + var url string + url += fmt.Sprintf("%s://%s", dsn.scheme, dsn.publicKey) + if dsn.secretKey != "" { + url += fmt.Sprintf(":%s", dsn.secretKey) + } + url += fmt.Sprintf("@%s", dsn.host) + if dsn.port != dsn.scheme.defaultPort() { + url += fmt.Sprintf(":%d", dsn.port) + } + if dsn.path != "" { + url += dsn.path + } + url += fmt.Sprintf("/%d", dsn.projectID) + return url +} + +// StoreAPIURL returns assembled url to be used in the transport. +// It points to configures Sentry instance. +func (dsn Dsn) StoreAPIURL() *url.URL { + var rawURL string + rawURL += fmt.Sprintf("%s://%s", dsn.scheme, dsn.host) + if dsn.port != dsn.scheme.defaultPort() { + rawURL += fmt.Sprintf(":%d", dsn.port) + } + rawURL += fmt.Sprintf("/api/%d/store/", dsn.projectID) + parsedURL, _ := url.Parse(rawURL) + return parsedURL +} + +// RequestHeaders returns all the necessary headers that have to be used in the transport. +func (dsn Dsn) RequestHeaders() map[string]string { + auth := fmt.Sprintf("Sentry sentry_version=%d, sentry_timestamp=%d, "+ + "sentry_client=sentry.go/%s, sentry_key=%s", 7, time.Now().Unix(), Version, dsn.publicKey) + + if dsn.secretKey != "" { + auth = fmt.Sprintf("%s, sentry_secret=%s", auth, dsn.secretKey) + } + + return map[string]string{ + "Content-Type": "application/json", + "X-Sentry-Auth": auth, + } +} + +func (dsn Dsn) MarshalJSON() ([]byte, error) { + return json.Marshal(dsn.String()) +} + +func (dsn *Dsn) UnmarshalJSON(data []byte) error { + var str string + _ = json.Unmarshal(data, &str) + newDsn, err := NewDsn(str) + if err != nil { + return err + } + *dsn = *newDsn + return nil +} diff --git a/vendor/github.com/getsentry/sentry-go/go.mod b/vendor/github.com/getsentry/sentry-go/go.mod new file mode 100644 index 0000000..a89feaf --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/go.mod @@ -0,0 +1,50 @@ +module github.com/getsentry/sentry-go + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/Joker/jade v1.0.0 // indirect + github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 // indirect + github.com/ajg/form v1.5.1 // indirect + github.com/aymerick/raymond v2.0.2+incompatible // indirect + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 // indirect + github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424 // indirect + github.com/gin-gonic/gin v1.4.0 + github.com/go-errors/errors v1.0.1 + github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab + github.com/google/go-querystring v1.0.0 // indirect + github.com/gorilla/schema v1.1.0 // indirect + github.com/imkira/go-interpol v1.1.0 // indirect + github.com/iris-contrib/blackfriday v2.0.0+incompatible // indirect + github.com/iris-contrib/formBinder v5.0.0+incompatible // indirect + github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect + github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce // indirect + github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect + github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40 // indirect + github.com/kataras/iris v11.1.1+incompatible + github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect + github.com/labstack/echo/v4 v4.1.10 + github.com/microcosm-cc/bluemonday v1.0.2 // indirect + github.com/moul/http2curl v1.0.0 // indirect + github.com/onsi/ginkgo v1.10.1 // indirect + github.com/onsi/gomega v1.7.0 // indirect + github.com/pingcap/errors v0.11.1 + github.com/pkg/errors v0.8.1 + github.com/ryanuber/columnize v2.1.0+incompatible // indirect + github.com/sergi/go-diff v1.0.0 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect + github.com/urfave/negroni v1.0.0 + github.com/valyala/fasthttp v1.4.0 + github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.1.0 // indirect + github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect + github.com/yudai/gojsondiff v1.0.0 // indirect + github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect + github.com/yudai/pp v2.0.1+incompatible // indirect +) + +go 1.11 diff --git a/vendor/github.com/getsentry/sentry-go/go.sum b/vendor/github.com/getsentry/sentry-go/go.sum new file mode 100644 index 0000000..71a5dce --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/go.sum @@ -0,0 +1,192 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a h1:PiDAizhfJbwZMISZ1Itx1ZTFeOFCml89Ofmz3V8rhoU= +github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE= +github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc= +github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0= +github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 h1:GY1+t5Dr9OKADM64SYnQjw/w99HMYvQ0A8/JoUkxVmc= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424 h1:Vh7rylVZRZCj6W41lRlP17xPk4Nq260H4Xo/DDYmEZk= +github.com/gavv/monotime v0.0.0-20190418164738-30dba4353424/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/formBinder v5.0.0+incompatible h1:jL+H+cCSEV8yzLwVbBI+tLRN/PpVatZtUZGK9ldi3bU= +github.com/iris-contrib/formBinder v5.0.0+incompatible/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w= +github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce h1:q8Ka/exfHNgK7izJE+aUOZd7KZXJ7oQbnJWiZakEiMo= +github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce/go.mod h1:VER17o2JZqquOx41avolD/wMGQSFEFBKWmhag9/RQRY= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618 h1:MK144iBQF9hTSwBW/9eJm034bVoG30IshVm688T2hi8= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40 h1:Q/QxpyNBtfkhXE68tnEA4yyqm77eh/3YOjOw875VbBY= +github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40/go.mod h1:PcaEvfvhGsqwXZ6S3CgCbmjcp+4UDUh2MIfF2ZEul8M= +github.com/kataras/iris v11.1.1+incompatible h1:c2iRKvKLpTYMXKdVB8YP/+A67NtZFt9kFFy+ZwBhWD0= +github.com/kataras/iris v11.1.1+incompatible/go.mod h1:ki9XPua5SyAJbIxDdsssxevgGrbpBmmvoQmo/A0IodY= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d h1:V5Rs9ztEWdp58oayPq/ulmlqJJZeJP6pP79uP3qjcao= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo/v4 v4.1.10 h1:/yhIpO50CBInUbE/nHJtGIyhBv0dJe2cDAYxc3V3uMo= +github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pingcap/errors v0.11.1 h1:BXFZ6MdDd2U1uJUa2sRAWTmm+nieEzuyYM0R4aUTcC8= +github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.4.0 h1:PuaTGZIw3mjYhhhbVbCQp8aciRZN9YdoB7MGX9Ko76A= +github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b h1:6cLsL+2FW6dRAdl5iMtHgRogVCff0QpRi9653YmdcJA= +github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/getsentry/sentry-go/hub.go b/vendor/github.com/getsentry/sentry-go/hub.go new file mode 100644 index 0000000..601564c --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/hub.go @@ -0,0 +1,336 @@ +package sentry + +import ( + "context" + "sync" + "time" +) + +type contextKey int + +// HubContextKey is a context key used to store Hub on any context.Context type +const HubContextKey = contextKey(1) + +// RequestContextKey is a context key used to store http.Request on the context passed to RecoverWithContext +const RequestContextKey = contextKey(2) + +// Default maximum number of breadcrumbs added to an event. Can be overwritten `maxBreadcrumbs` option. +const defaultMaxBreadcrumbs = 30 + +// Absolute maximum number of breadcrumbs added to an event. +// The `maxBreadcrumbs` option cannot be higher than this value. +const maxBreadcrumbs = 100 + +// Initial instance of the Hub that has no `Client` bound and an empty `Scope` +var currentHub = NewHub(nil, NewScope()) // nolint: gochecknoglobals + +// Hub is the central object that can manages scopes and clients. +// +// This can be used to capture events and manage the scope. +// The default hub that is available automatically. +// +// In most situations developers do not need to interface the hub. Instead +// toplevel convenience functions are exposed that will automatically dispatch +// to global (`CurrentHub`) hub. In some situations this might not be +// possible in which case it might become necessary to manually work with the +// hub. This is for instance the case when working with async code. +type Hub struct { + mu sync.RWMutex + stack *stack + lastEventID EventID +} + +type layer struct { + // mu protects concurrent reads and writes to client. + mu sync.RWMutex + client *Client + // scope is read-only, not protected by mu. + scope *Scope +} + +// Client returns the layer's client. Safe for concurrent use. +func (l *layer) Client() *Client { + l.mu.RLock() + defer l.mu.RUnlock() + return l.client +} + +// SetClient sets the layer's client. Safe for concurrent use. +func (l *layer) SetClient(c *Client) { + l.mu.Lock() + defer l.mu.Unlock() + l.client = c +} + +type stack []*layer + +// NewHub returns an instance of a `Hub` with provided `Client` and `Scope` bound. +func NewHub(client *Client, scope *Scope) *Hub { + hub := Hub{ + stack: &stack{{ + client: client, + scope: scope, + }}, + } + return &hub +} + +// CurrentHub returns an instance of previously initialized `Hub` stored in the global namespace. +func CurrentHub() *Hub { + return currentHub +} + +// LastEventID returns an ID of last captured event for the current `Hub`. +func (hub *Hub) LastEventID() EventID { + return hub.lastEventID +} + +func (hub *Hub) stackTop() *layer { + hub.mu.RLock() + defer hub.mu.RUnlock() + + stack := hub.stack + if stack == nil { + return nil + } + + stackLen := len(*stack) + if stackLen == 0 { + return nil + } + top := (*stack)[stackLen-1] + + return top +} + +// Clone returns a copy of the current Hub with top-most scope and client copied over. +func (hub *Hub) Clone() *Hub { + top := hub.stackTop() + if top == nil { + return nil + } + scope := top.scope + if scope != nil { + scope = scope.Clone() + } + return NewHub(top.Client(), scope) +} + +// Scope returns top-level `Scope` of the current `Hub` or `nil` if no `Scope` is bound. +func (hub *Hub) Scope() *Scope { + top := hub.stackTop() + if top == nil { + return nil + } + return top.scope +} + +// Scope returns top-level `Client` of the current `Hub` or `nil` if no `Client` is bound. +func (hub *Hub) Client() *Client { + top := hub.stackTop() + if top == nil { + return nil + } + return top.Client() +} + +// PushScope pushes a new scope for the current `Hub` and reuses previously bound `Client`. +func (hub *Hub) PushScope() *Scope { + top := hub.stackTop() + + var client *Client + if top != nil { + client = top.Client() + } + + var scope *Scope + if top != nil && top.scope != nil { + scope = top.scope.Clone() + } else { + scope = NewScope() + } + + hub.mu.Lock() + defer hub.mu.Unlock() + + *hub.stack = append(*hub.stack, &layer{ + client: client, + scope: scope, + }) + + return scope +} + +// PushScope pops the most recent scope for the current `Hub`. +func (hub *Hub) PopScope() { + hub.mu.Lock() + defer hub.mu.Unlock() + + stack := *hub.stack + stackLen := len(stack) + if stackLen > 0 { + *hub.stack = stack[0 : stackLen-1] + } +} + +// BindClient binds a new `Client` for the current `Hub`. +func (hub *Hub) BindClient(client *Client) { + top := hub.stackTop() + if top != nil { + top.SetClient(client) + } +} + +// WithScope temporarily pushes a scope for a single call. +// +// A shorthand for: +// PushScope() +// f(scope) +// PopScope() +func (hub *Hub) WithScope(f func(scope *Scope)) { + scope := hub.PushScope() + defer hub.PopScope() + f(scope) +} + +// ConfigureScope invokes a function that can modify the current scope. +// +// The function is passed a mutable reference to the `Scope` so that modifications +// can be performed. +func (hub *Hub) ConfigureScope(f func(scope *Scope)) { + scope := hub.Scope() + f(scope) +} + +// CaptureEvent calls the method of a same name on currently bound `Client` instance +// passing it a top-level `Scope`. +// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. +func (hub *Hub) CaptureEvent(event *Event) *EventID { + client, scope := hub.Client(), hub.Scope() + if client == nil || scope == nil { + return nil + } + return client.CaptureEvent(event, nil, scope) +} + +// CaptureMessage calls the method of a same name on currently bound `Client` instance +// passing it a top-level `Scope`. +// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. +func (hub *Hub) CaptureMessage(message string) *EventID { + client, scope := hub.Client(), hub.Scope() + if client == nil || scope == nil { + return nil + } + return client.CaptureMessage(message, nil, scope) +} + +// CaptureException calls the method of a same name on currently bound `Client` instance +// passing it a top-level `Scope`. +// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. +func (hub *Hub) CaptureException(exception error) *EventID { + client, scope := hub.Client(), hub.Scope() + if client == nil || scope == nil { + return nil + } + return client.CaptureException(exception, &EventHint{OriginalException: exception}, scope) +} + +// AddBreadcrumb records a new breadcrumb. +// +// The total number of breadcrumbs that can be recorded are limited by the +// configuration on the client. +func (hub *Hub) AddBreadcrumb(breadcrumb *Breadcrumb, hint *BreadcrumbHint) { + client := hub.Client() + + // If there's no client, just store it on the scope straight away + if client == nil { + hub.Scope().AddBreadcrumb(breadcrumb, maxBreadcrumbs) + return + } + + options := client.Options() + max := defaultMaxBreadcrumbs + + if options.MaxBreadcrumbs != 0 { + max = options.MaxBreadcrumbs + } + + if max < 0 { + return + } + + if options.BeforeBreadcrumb != nil { + h := &BreadcrumbHint{} + if hint != nil { + h = hint + } + if breadcrumb = options.BeforeBreadcrumb(breadcrumb, h); breadcrumb == nil { + Logger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.") + return + } + } + + if max > maxBreadcrumbs { + max = maxBreadcrumbs + } + hub.Scope().AddBreadcrumb(breadcrumb, max) +} + +// Recover calls the method of a same name on currently bound `Client` instance +// passing it a top-level `Scope`. +// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. +func (hub *Hub) Recover(err interface{}) *EventID { + if err == nil { + err = recover() + } + client, scope := hub.Client(), hub.Scope() + if client == nil || scope == nil { + return nil + } + return client.Recover(err, &EventHint{RecoveredException: err}, scope) +} + +// RecoverWithContext calls the method of a same name on currently bound `Client` instance +// passing it a top-level `Scope`. +// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. +func (hub *Hub) RecoverWithContext(ctx context.Context, err interface{}) *EventID { + if err == nil { + err = recover() + } + client, scope := hub.Client(), hub.Scope() + if client == nil || scope == nil { + return nil + } + return client.RecoverWithContext(ctx, err, &EventHint{RecoveredException: err}, scope) +} + +// Flush calls the method of a same name on currently bound `Client` instance. +func (hub *Hub) Flush(timeout time.Duration) bool { + client := hub.Client() + + if client == nil { + return false + } + + return client.Flush(timeout) +} + +// HasHubOnContext checks whether `Hub` instance is bound to a given `Context` struct. +func HasHubOnContext(ctx context.Context) bool { + _, ok := ctx.Value(HubContextKey).(*Hub) + return ok +} + +// GetHubFromContext tries to retrieve `Hub` instance from the given `Context` struct +// or return `nil` if one is not found. +func GetHubFromContext(ctx context.Context) *Hub { + if hub, ok := ctx.Value(HubContextKey).(*Hub); ok { + return hub + } + return nil +} + +// SetHubOnContext stores given `Hub` instance on the `Context` struct and returns a new `Context`. +func SetHubOnContext(ctx context.Context, hub *Hub) context.Context { + return context.WithValue(ctx, HubContextKey, hub) +} diff --git a/vendor/github.com/getsentry/sentry-go/integrations.go b/vendor/github.com/getsentry/sentry-go/integrations.go new file mode 100644 index 0000000..d9009d9 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/integrations.go @@ -0,0 +1,376 @@ +package sentry + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "regexp" + "runtime" + "strings" +) + +// ================================ +// Modules Integration +// ================================ + +type modulesIntegration struct{} + +var _modulesCache map[string]string // nolint: gochecknoglobals + +func (mi *modulesIntegration) Name() string { + return "Modules" +} + +func (mi *modulesIntegration) SetupOnce(client *Client) { + client.AddEventProcessor(mi.processor) +} + +func (mi *modulesIntegration) processor(event *Event, hint *EventHint) *Event { + if event.Modules == nil { + event.Modules = extractModules() + } + + return event +} + +func extractModules() map[string]string { + if _modulesCache != nil { + return _modulesCache + } + + extractedModules, err := getModules() + if err != nil { + Logger.Printf("ModuleIntegration wasn't able to extract modules: %v\n", err) + return nil + } + + _modulesCache = extractedModules + + return extractedModules +} + +func getModules() (map[string]string, error) { + if fileExists("go.mod") { + return getModulesFromMod() + } + + if fileExists("vendor") { + // Priority given to vendor created by modules + if fileExists("vendor/modules.txt") { + return getModulesFromVendorTxt() + } + + if fileExists("vendor/vendor.json") { + return getModulesFromVendorJSON() + } + } + + return nil, fmt.Errorf("module integration failed") +} + +func getModulesFromMod() (map[string]string, error) { + modules := make(map[string]string) + + file, err := os.Open("go.mod") + if err != nil { + return nil, fmt.Errorf("unable to open mod file") + } + + defer file.Close() + + areModulesPresent := false + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + splits := strings.Split(scanner.Text(), " ") + + if splits[0] == "require" { + areModulesPresent = true + + // Mod file has only 1 dependency + if len(splits) > 2 { + modules[strings.TrimSpace(splits[1])] = splits[2] + return modules, nil + } + } else if areModulesPresent && splits[0] != ")" { + modules[strings.TrimSpace(splits[0])] = splits[1] + } + } + + if scannerErr := scanner.Err(); scannerErr != nil { + return nil, scannerErr + } + + return modules, nil +} + +func getModulesFromVendorTxt() (map[string]string, error) { + modules := make(map[string]string) + + file, err := os.Open("vendor/modules.txt") + if err != nil { + return nil, fmt.Errorf("unable to open vendor/modules.txt") + } + + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + splits := strings.Split(scanner.Text(), " ") + + if splits[0] == "#" { + modules[splits[1]] = splits[2] + } + } + + if scannerErr := scanner.Err(); scannerErr != nil { + return nil, scannerErr + } + + return modules, nil +} + +func getModulesFromVendorJSON() (map[string]string, error) { + modules := make(map[string]string) + + file, err := ioutil.ReadFile("vendor/vendor.json") + + if err != nil { + return nil, fmt.Errorf("unable to open vendor/vendor.json") + } + + var vendor map[string]interface{} + if unmarshalErr := json.Unmarshal(file, &vendor); unmarshalErr != nil { + return nil, unmarshalErr + } + + packages := vendor["package"].([]interface{}) + + // To avoid iterative dependencies, TODO: Change of default value + lastPath := "\n" + + for _, value := range packages { + path := value.(map[string]interface{})["path"].(string) + + if !strings.Contains(path, lastPath) { + // No versions are available through vendor.json + modules[path] = "" + lastPath = path + } + } + + return modules, nil +} + +// ================================ +// Environment Integration +// ================================ + +type environmentIntegration struct{} + +func (ei *environmentIntegration) Name() string { + return "Environment" +} + +func (ei *environmentIntegration) SetupOnce(client *Client) { + client.AddEventProcessor(ei.processor) +} + +func (ei *environmentIntegration) processor(event *Event, hint *EventHint) *Event { + if event.Contexts == nil { + event.Contexts = make(map[string]interface{}) + } + + event.Contexts["device"] = map[string]interface{}{ + "arch": runtime.GOARCH, + "num_cpu": runtime.NumCPU(), + } + + event.Contexts["os"] = map[string]interface{}{ + "name": runtime.GOOS, + } + + event.Contexts["runtime"] = map[string]interface{}{ + "name": "go", + "version": runtime.Version(), + } + + return event +} + +// ================================ +// Ignore Errors Integration +// ================================ + +type ignoreErrorsIntegration struct { + ignoreErrors []*regexp.Regexp +} + +func (iei *ignoreErrorsIntegration) Name() string { + return "IgnoreErrors" +} + +func (iei *ignoreErrorsIntegration) SetupOnce(client *Client) { + iei.ignoreErrors = transformStringsIntoRegexps(client.Options().IgnoreErrors) + client.AddEventProcessor(iei.processor) +} + +func (iei *ignoreErrorsIntegration) processor(event *Event, hint *EventHint) *Event { + suspects := getIgnoreErrorsSuspects(event) + + for _, suspect := range suspects { + for _, pattern := range iei.ignoreErrors { + if pattern.Match([]byte(suspect)) { + Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+ + "| Value matched: %s | Filter used: %s", suspect, pattern) + return nil + } + } + } + + return event +} + +func transformStringsIntoRegexps(strings []string) []*regexp.Regexp { + var exprs []*regexp.Regexp + + for _, s := range strings { + r, err := regexp.Compile(s) + if err == nil { + exprs = append(exprs, r) + } + } + + return exprs +} + +func getIgnoreErrorsSuspects(event *Event) []string { + suspects := []string{} + + if event.Message != "" { + suspects = append(suspects, event.Message) + } + + for _, ex := range event.Exception { + suspects = append(suspects, ex.Type) + suspects = append(suspects, ex.Value) + } + + return suspects +} + +// ================================ +// Contextify Frames Integration +// ================================ + +type contextifyFramesIntegration struct { + sr sourceReader + contextLines int + cachedLocations map[string]string +} + +func (cfi *contextifyFramesIntegration) Name() string { + return "ContextifyFrames" +} + +func (cfi *contextifyFramesIntegration) SetupOnce(client *Client) { + cfi.sr = newSourceReader() + cfi.contextLines = 5 + cfi.cachedLocations = make(map[string]string) + + client.AddEventProcessor(cfi.processor) +} + +func (cfi *contextifyFramesIntegration) processor(event *Event, hint *EventHint) *Event { + // Range over all exceptions + for _, ex := range event.Exception { + // If it has no stacktrace, just bail out + if ex.Stacktrace == nil { + continue + } + + // If it does, it should have frames, so try to contextify them + ex.Stacktrace.Frames = cfi.contextify(ex.Stacktrace.Frames) + } + + // Range over all threads + for _, th := range event.Threads { + // If it has no stacktrace, just bail out + if th.Stacktrace == nil { + continue + } + + // If it does, it should have frames, so try to contextify them + th.Stacktrace.Frames = cfi.contextify(th.Stacktrace.Frames) + } + + return event +} + +func (cfi *contextifyFramesIntegration) contextify(frames []Frame) []Frame { + contextifiedFrames := make([]Frame, 0, len(frames)) + + for _, frame := range frames { + if !frame.InApp { + contextifiedFrames = append(contextifiedFrames, frame) + continue + } + + var path string + + if cachedPath, ok := cfi.cachedLocations[frame.AbsPath]; ok { + path = cachedPath + } else { + // Optimize for happy path here + if fileExists(frame.AbsPath) { + path = frame.AbsPath + } else { + path = cfi.findNearbySourceCodeLocation(frame.AbsPath) + } + } + + if path == "" { + contextifiedFrames = append(contextifiedFrames, frame) + continue + } + + lines, contextLine := cfi.sr.readContextLines(path, frame.Lineno, cfi.contextLines) + contextifiedFrames = append(contextifiedFrames, cfi.addContextLinesToFrame(frame, lines, contextLine)) + } + + return contextifiedFrames +} + +func (cfi *contextifyFramesIntegration) findNearbySourceCodeLocation(originalPath string) string { + trimmedPath := strings.TrimPrefix(originalPath, "/") + components := strings.Split(trimmedPath, "/") + + for len(components) > 0 { + components = components[1:] + possibleLocation := strings.Join(components, "/") + + if fileExists(possibleLocation) { + cfi.cachedLocations[originalPath] = possibleLocation + return possibleLocation + } + } + + cfi.cachedLocations[originalPath] = "" + return "" +} + +func (cfi *contextifyFramesIntegration) addContextLinesToFrame(frame Frame, lines [][]byte, contextLine int) Frame { + for i, line := range lines { + switch { + case i < contextLine: + frame.PreContext = append(frame.PreContext, string(line)) + case i == contextLine: + frame.ContextLine = string(line) + default: + frame.PostContext = append(frame.PostContext, string(line)) + } + } + return frame +} diff --git a/vendor/github.com/getsentry/sentry-go/interfaces.go b/vendor/github.com/getsentry/sentry-go/interfaces.go new file mode 100644 index 0000000..c3d7ef5 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/interfaces.go @@ -0,0 +1,180 @@ +package sentry + +import ( + "context" + "fmt" + "io/ioutil" + "net" + "net/http" + "strings" +) + +// Protocol Docs (kinda) +// https://github.com/getsentry/rust-sentry-types/blob/master/src/protocol/v7.rs + +// Level marks the severity of the event +type Level string + +const ( + LevelDebug Level = "debug" + LevelInfo Level = "info" + LevelWarning Level = "warning" + LevelError Level = "error" + LevelFatal Level = "fatal" +) + +// https://docs.sentry.io/development/sdk-dev/interfaces/sdk/ +type SdkInfo struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Integrations []string `json:"integrations,omitempty"` + Packages []SdkPackage `json:"packages,omitempty"` +} + +type SdkPackage struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` +} + +// TODO: This type could be more useful, as map of interface{} is too generic +// and requires a lot of type assertions in beforeBreadcrumb calls +// plus it could just be `map[string]interface{}` then +type BreadcrumbHint map[string]interface{} + +// https://docs.sentry.io/development/sdk-dev/interfaces/breadcrumbs/ +type Breadcrumb struct { + Category string `json:"category,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` + Level Level `json:"level,omitempty"` + Message string `json:"message,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Type string `json:"type,omitempty"` +} + +// https://docs.sentry.io/development/sdk-dev/interfaces/user/ +type User struct { + Email string `json:"email,omitempty"` + ID string `json:"id,omitempty"` + IPAddress string `json:"ip_address,omitempty"` + Username string `json:"username,omitempty"` +} + +// https://docs.sentry.io/development/sdk-dev/interfaces/http/ +type Request struct { + URL string `json:"url,omitempty"` + Method string `json:"method,omitempty"` + Data string `json:"data,omitempty"` + QueryString string `json:"query_string,omitempty"` + Cookies string `json:"cookies,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Env map[string]string `json:"env,omitempty"` +} + +func (r Request) FromHTTPRequest(request *http.Request) Request { + // Method + r.Method = request.Method + + // URL + protocol := schemeHTTP + if request.TLS != nil || request.Header.Get("X-Forwarded-Proto") == "https" { + protocol = schemeHTTPS + } + r.URL = fmt.Sprintf("%s://%s%s", protocol, request.Host, request.URL.Path) + + // Headers + headers := make(map[string]string, len(request.Header)) + for k, v := range request.Header { + headers[k] = strings.Join(v, ",") + } + headers["Host"] = request.Host + r.Headers = headers + + // Cookies + r.Cookies = request.Header.Get("Cookie") + + // Env + if addr, port, err := net.SplitHostPort(request.RemoteAddr); err == nil { + r.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port} + } + + // QueryString + r.QueryString = request.URL.RawQuery + + // Body + if request.GetBody != nil { + if bodyCopy, err := request.GetBody(); err == nil && bodyCopy != nil { + body, err := ioutil.ReadAll(bodyCopy) + if err == nil { + r.Data = string(body) + } + } + } + + return r +} + +// https://docs.sentry.io/development/sdk-dev/interfaces/exception/ +type Exception struct { + Type string `json:"type,omitempty"` + Value string `json:"value,omitempty"` + Module string `json:"module,omitempty"` + Stacktrace *Stacktrace `json:"stacktrace,omitempty"` + RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"` +} + +type EventID string + +// https://docs.sentry.io/development/sdk-dev/attributes/ +type Event struct { + Breadcrumbs []*Breadcrumb `json:"breadcrumbs,omitempty"` + Contexts map[string]interface{} `json:"contexts,omitempty"` + Dist string `json:"dist,omitempty"` + Environment string `json:"environment,omitempty"` + EventID EventID `json:"event_id,omitempty"` + Extra map[string]interface{} `json:"extra,omitempty"` + Fingerprint []string `json:"fingerprint,omitempty"` + Level Level `json:"level,omitempty"` + Message string `json:"message,omitempty"` + Platform string `json:"platform,omitempty"` + Release string `json:"release,omitempty"` + Sdk SdkInfo `json:"sdk,omitempty"` + ServerName string `json:"server_name,omitempty"` + Threads []Thread `json:"threads,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Transaction string `json:"transaction,omitempty"` + User User `json:"user,omitempty"` + Logger string `json:"logger,omitempty"` + Modules map[string]string `json:"modules,omitempty"` + Request Request `json:"request,omitempty"` + Exception []Exception `json:"exception,omitempty"` +} + +func NewEvent() *Event { + event := Event{ + Contexts: make(map[string]interface{}), + Extra: make(map[string]interface{}), + Tags: make(map[string]string), + Modules: make(map[string]string), + } + return &event +} + +type Thread struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Stacktrace *Stacktrace `json:"stacktrace,omitempty"` + RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"` + Crashed bool `json:"crashed,omitempty"` + Current bool `json:"current,omitempty"` +} + +type EventHint struct { + Data interface{} + EventID string + OriginalException error + RecoveredException interface{} + Context context.Context + Request *http.Request + Response *http.Response +} diff --git a/vendor/github.com/getsentry/sentry-go/scope.go b/vendor/github.com/getsentry/sentry-go/scope.go new file mode 100644 index 0000000..5aa628e --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/scope.go @@ -0,0 +1,309 @@ +package sentry + +import ( + "reflect" + "sync" + "time" +) + +// Scope holds contextual data for the current scope. +// +// The scope is an object that can cloned efficiently and stores data that +// is locally relevant to an event. For instance the scope will hold recorded +// breadcrumbs and similar information. +// +// The scope can be interacted with in two ways: +// +// 1. the scope is routinely updated with information by functions such as +// `AddBreadcrumb` which will modify the currently top-most scope. +// 2. the topmost scope can also be configured through the `ConfigureScope` +// method. +// +// Note that the scope can only be modified but not inspected. +// Only the client can use the scope to extract information currently. +type Scope struct { + mu sync.RWMutex + breadcrumbs []*Breadcrumb + user User + tags map[string]string + contexts map[string]interface{} + extra map[string]interface{} + fingerprint []string + level Level + transaction string + request Request + eventProcessors []EventProcessor +} + +func NewScope() *Scope { + scope := Scope{ + breadcrumbs: make([]*Breadcrumb, 0), + tags: make(map[string]string), + contexts: make(map[string]interface{}), + extra: make(map[string]interface{}), + fingerprint: make([]string, 0), + } + + return &scope +} + +// AddBreadcrumb adds new breadcrumb to the current scope +// and optionaly throws the old one if limit is reached. +func (scope *Scope) AddBreadcrumb(breadcrumb *Breadcrumb, limit int) { + if breadcrumb.Timestamp == 0 { + breadcrumb.Timestamp = time.Now().Unix() + } + + scope.mu.Lock() + defer scope.mu.Unlock() + + breadcrumbs := append(scope.breadcrumbs, breadcrumb) + if len(breadcrumbs) > limit { + scope.breadcrumbs = breadcrumbs[1 : limit+1] + } else { + scope.breadcrumbs = breadcrumbs + } +} + +// ClearBreadcrumbs clears all breadcrumbs from the current scope. +func (scope *Scope) ClearBreadcrumbs() { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.breadcrumbs = []*Breadcrumb{} +} + +// SetUser sets new user for the current scope. +func (scope *Scope) SetUser(user User) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.user = user +} + +// SetRequest sets new user for the current scope. +func (scope *Scope) SetRequest(request Request) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.request = request +} + +// SetTag adds a tag to the current scope. +func (scope *Scope) SetTag(key, value string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.tags[key] = value +} + +// SetTags assigns multiple tags to the current scope. +func (scope *Scope) SetTags(tags map[string]string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + for k, v := range tags { + scope.tags[k] = v + } +} + +// RemoveTag removes a tag from the current scope. +func (scope *Scope) RemoveTag(key string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + delete(scope.tags, key) +} + +// SetContext adds a context to the current scope. +func (scope *Scope) SetContext(key string, value interface{}) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.contexts[key] = value +} + +// SetContexts assigns multiple contexts to the current scope. +func (scope *Scope) SetContexts(contexts map[string]interface{}) { + scope.mu.Lock() + defer scope.mu.Unlock() + + for k, v := range contexts { + scope.contexts[k] = v + } +} + +// RemoveContext removes a context from the current scope. +func (scope *Scope) RemoveContext(key string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + delete(scope.contexts, key) +} + +// SetExtra adds an extra to the current scope. +func (scope *Scope) SetExtra(key string, value interface{}) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.extra[key] = value +} + +// SetExtras assigns multiple extras to the current scope. +func (scope *Scope) SetExtras(extra map[string]interface{}) { + scope.mu.Lock() + defer scope.mu.Unlock() + + for k, v := range extra { + scope.extra[k] = v + } +} + +// RemoveExtra removes a extra from the current scope. +func (scope *Scope) RemoveExtra(key string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + delete(scope.extra, key) +} + +// SetFingerprint sets new fingerprint for the current scope. +func (scope *Scope) SetFingerprint(fingerprint []string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.fingerprint = fingerprint +} + +// SetLevel sets new level for the current scope. +func (scope *Scope) SetLevel(level Level) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.level = level +} + +// SetTransaction sets new transaction name for the current transaction. +func (scope *Scope) SetTransaction(transactionName string) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.transaction = transactionName +} + +// Clone returns a copy of the current scope with all data copied over. +func (scope *Scope) Clone() *Scope { + scope.mu.RLock() + defer scope.mu.RUnlock() + + clone := NewScope() + clone.user = scope.user + clone.breadcrumbs = make([]*Breadcrumb, len(scope.breadcrumbs)) + copy(clone.breadcrumbs, scope.breadcrumbs) + for key, value := range scope.tags { + clone.tags[key] = value + } + for key, value := range scope.contexts { + clone.contexts[key] = value + } + for key, value := range scope.extra { + clone.extra[key] = value + } + clone.fingerprint = make([]string, len(scope.fingerprint)) + copy(clone.fingerprint, scope.fingerprint) + clone.level = scope.level + clone.transaction = scope.transaction + clone.request = scope.request + + return clone +} + +// Clear removes the data from the current scope. Not safe for concurrent use. +func (scope *Scope) Clear() { + *scope = *NewScope() +} + +// AddEventProcessor adds an event processor to the current scope. +func (scope *Scope) AddEventProcessor(processor EventProcessor) { + scope.mu.Lock() + defer scope.mu.Unlock() + + scope.eventProcessors = append(scope.eventProcessors, processor) +} + +// ApplyToEvent takes the data from the current scope and attaches it to the event. +func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint) *Event { + scope.mu.RLock() + defer scope.mu.RUnlock() + + if len(scope.breadcrumbs) > 0 { + if event.Breadcrumbs == nil { + event.Breadcrumbs = []*Breadcrumb{} + } + + event.Breadcrumbs = append(event.Breadcrumbs, scope.breadcrumbs...) + } + + if len(scope.tags) > 0 { + if event.Tags == nil { + event.Tags = make(map[string]string) + } + + for key, value := range scope.tags { + event.Tags[key] = value + } + } + + if len(scope.contexts) > 0 { + if event.Contexts == nil { + event.Contexts = make(map[string]interface{}) + } + + for key, value := range scope.contexts { + event.Contexts[key] = value + } + } + + if len(scope.extra) > 0 { + if event.Extra == nil { + event.Extra = make(map[string]interface{}) + } + + for key, value := range scope.extra { + event.Extra[key] = value + } + } + + if (reflect.DeepEqual(event.User, User{})) { + event.User = scope.user + } + + if (event.Fingerprint == nil || len(event.Fingerprint) == 0) && + len(scope.fingerprint) > 0 { + event.Fingerprint = make([]string, len(scope.fingerprint)) + copy(event.Fingerprint, scope.fingerprint) + } + + if scope.level != "" { + event.Level = scope.level + } + + if scope.transaction != "" { + event.Transaction = scope.transaction + } + + if (reflect.DeepEqual(event.Request, Request{})) { + event.Request = scope.request + } + + for _, processor := range scope.eventProcessors { + id := event.EventID + event = processor(event, hint) + if event == nil { + Logger.Printf("Event dropped by one of the Scope EventProcessors: %s\n", id) + return nil + } + } + + return event +} diff --git a/vendor/github.com/getsentry/sentry-go/sentry.go b/vendor/github.com/getsentry/sentry-go/sentry.go new file mode 100644 index 0000000..efada45 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/sentry.go @@ -0,0 +1,122 @@ +package sentry + +import ( + "context" + "time" +) + +// Version Sentry-Go SDK Version +const Version = "0.3.0" + +// Init initializes whole SDK by creating new `Client` and binding it to the current `Hub` +func Init(options ClientOptions) error { + hub := CurrentHub() + client, err := NewClient(options) + if err != nil { + return err + } + hub.BindClient(client) + return nil +} + +// AddBreadcrumb records a new breadcrumb. +// +// The total number of breadcrumbs that can be recorded are limited by the +// configuration on the client. +func AddBreadcrumb(breadcrumb *Breadcrumb) { + hub := CurrentHub() + hub.AddBreadcrumb(breadcrumb, nil) +} + +// CaptureMessage captures an arbitrary message. +func CaptureMessage(message string) *EventID { + hub := CurrentHub() + return hub.CaptureMessage(message) +} + +// CaptureException captures an error. +func CaptureException(exception error) *EventID { + hub := CurrentHub() + return hub.CaptureException(exception) +} + +// CaptureEvent captures an event on the currently active client if any. +// +// The event must already be assembled. Typically code would instead use +// the utility methods like `CaptureException`. The return value is the +// event ID. In case Sentry is disabled or event was dropped, the return value will be nil. +func CaptureEvent(event *Event) *EventID { + hub := CurrentHub() + return hub.CaptureEvent(event) +} + +// Recover captures a panic. +func Recover() *EventID { + if err := recover(); err != nil { + hub := CurrentHub() + return hub.Recover(err) + } + return nil +} + +// Recover captures a panic and passes relevant context object. +func RecoverWithContext(ctx context.Context) *EventID { + if err := recover(); err != nil { + var hub *Hub + + if HasHubOnContext(ctx) { + hub = GetHubFromContext(ctx) + } else { + hub = CurrentHub() + } + + return hub.RecoverWithContext(ctx, err) + } + return nil +} + +// WithScope temporarily pushes a scope for a single call. +// +// This function takes one argument, a callback that executes +// in the context of that scope. +// +// This is useful when extra data should be send with a single capture call +// for instance a different level or tags +func WithScope(f func(scope *Scope)) { + hub := CurrentHub() + hub.WithScope(f) +} + +// ConfigureScope invokes a function that can modify the current scope. +// +// The function is passed a mutable reference to the `Scope` so that modifications +// can be performed. +func ConfigureScope(f func(scope *Scope)) { + hub := CurrentHub() + hub.ConfigureScope(f) +} + +// PushScope pushes a new scope. +func PushScope() { + hub := CurrentHub() + hub.PushScope() +} + +// PopScope pushes a new scope. +func PopScope() { + hub := CurrentHub() + hub.PopScope() +} + +// Flush notifies when all the buffered events have been sent by returning `true` +// or `false` if timeout was reached. +func Flush(timeout time.Duration) bool { + hub := CurrentHub() + return hub.Flush(timeout) +} + +// LastEventID returns an ID of last captured event. +func LastEventID() EventID { + hub := CurrentHub() + return hub.LastEventID() +} diff --git a/vendor/github.com/getsentry/sentry-go/sourcereader.go b/vendor/github.com/getsentry/sentry-go/sourcereader.go new file mode 100644 index 0000000..2d35768 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/sourcereader.go @@ -0,0 +1,69 @@ +package sentry + +import ( + "bytes" + "io/ioutil" + "sync" +) + +type sourceReader struct { + mu sync.Mutex + cache map[string][][]byte +} + +func newSourceReader() sourceReader { + return sourceReader{ + cache: make(map[string][][]byte), + } +} + +func (sr *sourceReader) readContextLines(filename string, line, context int) ([][]byte, int) { + sr.mu.Lock() + defer sr.mu.Unlock() + + lines, ok := sr.cache[filename] + + if !ok { + data, err := ioutil.ReadFile(filename) + if err != nil { + sr.cache[filename] = nil + return nil, 0 + } + lines = bytes.Split(data, []byte{'\n'}) + sr.cache[filename] = lines + } + + return sr.calculateContextLines(lines, line, context) +} + +// `contextLine` points to a line that caused an issue itself, in relation to returned slice +func (sr *sourceReader) calculateContextLines(lines [][]byte, line, context int) ([][]byte, int) { + // Stacktrace lines are 1-indexed, slices are 0-indexed + line-- + + contextLine := context + + if lines == nil || line >= len(lines) || line < 0 { + return nil, 0 + } + + if context < 0 { + context = 0 + contextLine = 0 + } + + start := line - context + + if start < 0 { + contextLine += start + start = 0 + } + + end := line + context + 1 + + if end > len(lines) { + end = len(lines) + } + + return lines[start:end], contextLine +} diff --git a/vendor/github.com/getsentry/sentry-go/stacktrace.go b/vendor/github.com/getsentry/sentry-go/stacktrace.go new file mode 100644 index 0000000..25fdb76 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/stacktrace.go @@ -0,0 +1,261 @@ +package sentry + +import ( + "go/build" + "path/filepath" + "reflect" + "regexp" + "runtime" + "strings" +) + +const unknown string = "unknown" + +// nolint: gochecknoglobals +var ( + isTestFileRegexp = regexp.MustCompile(`getsentry/sentry-go/.+_test.go`) + isExampleFileRegexp = regexp.MustCompile(`getsentry/sentry-go/example/`) +) + +// The module download is split into two parts: downloading the go.mod and downloading the actual code. +// If you have dependencies only needed for tests, then they will show up in your go.mod, +// and go get will download their go.mods, but it will not download their code. +// The test-only dependencies get downloaded only when you need it, such as the first time you run go test. +// +// https://github.com/golang/go/issues/26913#issuecomment-411976222 + +// Stacktrace holds information about the frames of the stack. +type Stacktrace struct { + Frames []Frame `json:"frames,omitempty"` + FramesOmitted []uint `json:"frames_omitted,omitempty"` +} + +// NewStacktrace creates a stacktrace using `runtime.Callers`. +func NewStacktrace() *Stacktrace { + pcs := make([]uintptr, 100) + n := runtime.Callers(1, pcs) + + if n == 0 { + return nil + } + + frames := extractFrames(pcs[:n]) + frames = filterFrames(frames) + + stacktrace := Stacktrace{ + Frames: frames, + } + + return &stacktrace +} + +// ExtractStacktrace creates a new `Stacktrace` based on the given `error` object. +// TODO: Make it configurable so that anyone can provide their own implementation? +// Use of reflection allows us to not have a hard dependency on any given package, so we don't have to import it +func ExtractStacktrace(err error) *Stacktrace { + method := extractReflectedStacktraceMethod(err) + + if !method.IsValid() { + return nil + } + + pcs := extractPcs(method) + + if len(pcs) == 0 { + return nil + } + + frames := extractFrames(pcs) + frames = filterFrames(frames) + + stacktrace := Stacktrace{ + Frames: frames, + } + + return &stacktrace +} + +func extractReflectedStacktraceMethod(err error) reflect.Value { + var method reflect.Value + + // https://github.com/pingcap/errors + methodGetStackTracer := reflect.ValueOf(err).MethodByName("GetStackTracer") + // https://github.com/pkg/errors + methodStackTrace := reflect.ValueOf(err).MethodByName("StackTrace") + // https://github.com/go-errors/errors + methodStackFrames := reflect.ValueOf(err).MethodByName("StackFrames") + + if methodGetStackTracer.IsValid() { + stacktracer := methodGetStackTracer.Call(make([]reflect.Value, 0))[0] + stacktracerStackTrace := reflect.ValueOf(stacktracer).MethodByName("StackTrace") + + if stacktracerStackTrace.IsValid() { + method = stacktracerStackTrace + } + } + + if methodStackTrace.IsValid() { + method = methodStackTrace + } + + if methodStackFrames.IsValid() { + method = methodStackFrames + } + + return method +} + +func extractPcs(method reflect.Value) []uintptr { + var pcs []uintptr + + stacktrace := method.Call(make([]reflect.Value, 0))[0] + + if stacktrace.Kind() != reflect.Slice { + return nil + } + + for i := 0; i < stacktrace.Len(); i++ { + pc := stacktrace.Index(i) + + if pc.Kind() == reflect.Uintptr { + pcs = append(pcs, uintptr(pc.Uint())) + continue + } + + if pc.Kind() == reflect.Struct { + field := pc.FieldByName("ProgramCounter") + if field.IsValid() && field.Kind() == reflect.Uintptr { + pcs = append(pcs, uintptr(field.Uint())) + continue + } + } + } + + return pcs +} + +// https://docs.sentry.io/development/sdk-dev/interfaces/stacktrace/ +type Frame struct { + Function string `json:"function,omitempty"` + Symbol string `json:"symbol,omitempty"` + Module string `json:"module,omitempty"` + Package string `json:"package,omitempty"` + Filename string `json:"filename,omitempty"` + AbsPath string `json:"abs_path,omitempty"` + Lineno int `json:"lineno,omitempty"` + Colno int `json:"colno,omitempty"` + PreContext []string `json:"pre_context,omitempty"` + ContextLine string `json:"context_line,omitempty"` + PostContext []string `json:"post_context,omitempty"` + InApp bool `json:"in_app,omitempty"` + Vars map[string]interface{} `json:"vars,omitempty"` +} + +// NewFrame assembles a stacktrace frame out of `runtime.Frame`. +func NewFrame(f runtime.Frame) Frame { + abspath := f.File + filename := f.File + function := f.Function + var module string + + if filename != "" { + filename = extractFilename(filename) + } else { + filename = unknown + } + + if abspath == "" { + abspath = unknown + } + + if function != "" { + module, function = deconstructFunctionName(function) + } + + frame := Frame{ + AbsPath: abspath, + Filename: filename, + Lineno: f.Line, + Module: module, + Function: function, + } + + frame.InApp = isInAppFrame(frame) + + return frame +} + +func extractFrames(pcs []uintptr) []Frame { + var frames []Frame + callersFrames := runtime.CallersFrames(pcs) + + for { + callerFrame, more := callersFrames.Next() + + frames = append([]Frame{ + NewFrame(callerFrame), + }, frames...) + + if !more { + break + } + } + + return frames +} + +func filterFrames(frames []Frame) []Frame { + filteredFrames := make([]Frame, 0, len(frames)) + + for _, frame := range frames { + // go runtime frames + if frame.Module == "runtime" || frame.Module == "testing" { + continue + } + // sentry internal frames + isTestFile := isTestFileRegexp.MatchString(frame.AbsPath) + isExampleFile := isExampleFileRegexp.MatchString(frame.AbsPath) + if strings.Contains(frame.AbsPath, "github.com/getsentry/sentry-go") && + !isTestFile && + !isExampleFile { + continue + } + filteredFrames = append(filteredFrames, frame) + } + + return filteredFrames +} + +func extractFilename(path string) string { + _, file := filepath.Split(path) + return file +} + +func isInAppFrame(frame Frame) bool { + if strings.HasPrefix(frame.AbsPath, build.Default.GOROOT) || + strings.Contains(frame.Module, "vendor") || + strings.Contains(frame.Module, "third_party") { + return false + } + + return true +} + +// Transform `runtime/debug.*T·ptrmethod` into `{ module: runtime/debug, function: *T.ptrmethod }` +func deconstructFunctionName(name string) (module string, function string) { + if idx := strings.LastIndex(name, "."); idx != -1 { + module = name[:idx] + function = name[idx+1:] + } + function = strings.Replace(function, "·", ".", -1) + return module, function +} + +func callerFunctionName() string { + pcs := make([]uintptr, 1) + runtime.Callers(3, pcs) + callersFrames := runtime.CallersFrames(pcs) + callerFrame, _ := callersFrames.Next() + _, function := deconstructFunctionName(callerFrame.Function) + return function +} diff --git a/vendor/github.com/getsentry/sentry-go/transport.go b/vendor/github.com/getsentry/sentry-go/transport.go new file mode 100644 index 0000000..75f158a --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/transport.go @@ -0,0 +1,350 @@ +package sentry + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "net/http" + "net/url" + "strconv" + "sync" + "time" +) + +const defaultBufferSize = 30 +const defaultRetryAfter = time.Second * 60 +const defaultTimeout = time.Second * 30 + +// Transport is used by the `Client` to deliver events to remote server. +type Transport interface { + Flush(timeout time.Duration) bool + Configure(options ClientOptions) + SendEvent(event *Event) +} + +func getProxyConfig(options ClientOptions) func(*http.Request) (*url.URL, error) { + if options.HTTPSProxy != "" { + return func(_ *http.Request) (*url.URL, error) { + return url.Parse(options.HTTPSProxy) + } + } else if options.HTTPProxy != "" { + return func(_ *http.Request) (*url.URL, error) { + return url.Parse(options.HTTPProxy) + } + } + + return http.ProxyFromEnvironment +} + +func getTLSConfig(options ClientOptions) *tls.Config { + if options.CaCerts != nil { + return &tls.Config{ + RootCAs: options.CaCerts, + } + } + + return nil +} + +func retryAfter(now time.Time, r *http.Response) time.Duration { + retryAfterHeader := r.Header["Retry-After"] + + if retryAfterHeader == nil { + return defaultRetryAfter + } + + if date, err := time.Parse(time.RFC1123, retryAfterHeader[0]); err == nil { + return date.Sub(now) + } + + if seconds, err := strconv.Atoi(retryAfterHeader[0]); err == nil { + return time.Second * time.Duration(seconds) + } + + return defaultRetryAfter +} + +func getRequestBodyFromEvent(event *Event) []byte { + body, err := json.Marshal(event) + if err == nil { + return body + } + + partialMarshallMessage := "Original event couldn't be marshalled. Succeeded by stripping the data " + + "that uses interface{} type. Please verify that the data you attach to the scope is serializable." + // Try to serialize the event, with all the contextudal data that allows for interface{} stripped. + event.Breadcrumbs = nil + event.Contexts = nil + event.Extra = map[string]interface{}{ + "info": partialMarshallMessage, + } + body, err = json.Marshal(event) + if err == nil { + Logger.Println(partialMarshallMessage) + return body + } + + // This should _only_ happen when Event.Exception[0].Stacktrace.Frames[0].Vars is unserializable + // Which won't ever happen, as we don't use it now (although it's the part of public interface accepted by Sentry) + // Juuust in case something, somehow goes uterly wrong. + Logger.Println("Event couldn't be marshalled, even with stripped contextual data. Skipping delivery. " + + "Please notify the SDK owners with possibly broken payload.") + return nil +} + +// ================================ +// HTTPTransport +// ================================ + +// HTTPTransport is a default implementation of `Transport` interface used by `Client`. +type HTTPTransport struct { + dsn *Dsn + client *http.Client + transport *http.Transport + + buffer chan *http.Request + disabledUntil time.Time + + wg sync.WaitGroup + start sync.Once + + // Size of the transport buffer. Defaults to 30. + BufferSize int + // HTTP Client request timeout. Defaults to 30 seconds. + Timeout time.Duration +} + +// NewHTTPTransport returns a new pre-configured instance of HTTPTransport +func NewHTTPTransport() *HTTPTransport { + transport := HTTPTransport{ + BufferSize: defaultBufferSize, + Timeout: defaultTimeout, + } + return &transport +} + +// Configure is called by the `Client` itself, providing it it's own `ClientOptions`. +func (t *HTTPTransport) Configure(options ClientOptions) { + dsn, err := NewDsn(options.Dsn) + if err != nil { + Logger.Printf("%v\n", err) + return + } + + t.dsn = dsn + t.buffer = make(chan *http.Request, t.BufferSize) + + if options.HTTPTransport != nil { + t.transport = options.HTTPTransport + } else { + t.transport = &http.Transport{ + Proxy: getProxyConfig(options), + TLSClientConfig: getTLSConfig(options), + } + } + + t.client = &http.Client{ + Transport: t.transport, + Timeout: t.Timeout, + } + + t.start.Do(func() { + go t.worker() + }) +} + +// SendEvent assembles a new packet out of `Event` and sends it to remote server. +func (t *HTTPTransport) SendEvent(event *Event) { + if t.dsn == nil || time.Now().Before(t.disabledUntil) { + return + } + + body := getRequestBodyFromEvent(event) + if body == nil { + return + } + + request, _ := http.NewRequest( + http.MethodPost, + t.dsn.StoreAPIURL().String(), + bytes.NewBuffer(body), + ) + + for headerKey, headerValue := range t.dsn.RequestHeaders() { + request.Header.Set(headerKey, headerValue) + } + + t.wg.Add(1) + + select { + case t.buffer <- request: + Logger.Printf( + "Sending %s event [%s] to %s project: %d\n", + event.Level, + event.EventID, + t.dsn.host, + t.dsn.projectID, + ) + default: + t.wg.Done() + Logger.Println("Event dropped due to transport buffer being full.") + // worker would block, drop the packet + } +} + +// Flush notifies when all the buffered events have been sent by returning `true` +// or `false` if timeout was reached. +func (t *HTTPTransport) Flush(timeout time.Duration) bool { + c := make(chan struct{}) + + go func() { + t.wg.Wait() + close(c) + }() + + select { + case <-c: + Logger.Println("Buffer flushed successfully.") + return true + case <-time.After(timeout): + Logger.Println("Buffer flushing reached the timeout.") + return false + } +} + +func (t *HTTPTransport) worker() { + for request := range t.buffer { + if time.Now().Before(t.disabledUntil) { + t.wg.Done() + continue + } + + response, err := t.client.Do(request) + + if err != nil { + Logger.Printf("There was an issue with sending an event: %v", err) + } + + if response != nil && response.StatusCode == http.StatusTooManyRequests { + t.disabledUntil = time.Now().Add(retryAfter(time.Now(), response)) + Logger.Printf("Too many requests, backing off till: %s\n", t.disabledUntil) + } + + t.wg.Done() + } +} + +// ================================ +// HTTPSyncTransport +// ================================ + +// HTTPSyncTransport is an implementation of `Transport` interface which blocks after each captured event. +type HTTPSyncTransport struct { + dsn *Dsn + client *http.Client + transport *http.Transport + disabledUntil time.Time + + // HTTP Client request timeout. Defaults to 30 seconds. + Timeout time.Duration +} + +// NewHTTPSyncTransport returns a new pre-configured instance of HTTPSyncTransport +func NewHTTPSyncTransport() *HTTPSyncTransport { + transport := HTTPSyncTransport{ + Timeout: defaultTimeout, + } + + return &transport +} + +// Configure is called by the `Client` itself, providing it it's own `ClientOptions`. +func (t *HTTPSyncTransport) Configure(options ClientOptions) { + dsn, err := NewDsn(options.Dsn) + if err != nil { + Logger.Printf("%v\n", err) + return + } + t.dsn = dsn + + if options.HTTPTransport != nil { + t.transport = options.HTTPTransport + } else { + t.transport = &http.Transport{ + Proxy: getProxyConfig(options), + TLSClientConfig: getTLSConfig(options), + } + } + + t.client = &http.Client{ + Transport: t.transport, + Timeout: t.Timeout, + } +} + +// SendEvent assembles a new packet out of `Event` and sends it to remote server. +func (t *HTTPSyncTransport) SendEvent(event *Event) { + if t.dsn == nil || time.Now().Before(t.disabledUntil) { + return + } + + body := getRequestBodyFromEvent(event) + if body == nil { + return + } + + request, _ := http.NewRequest( + http.MethodPost, + t.dsn.StoreAPIURL().String(), + bytes.NewBuffer(body), + ) + + for headerKey, headerValue := range t.dsn.RequestHeaders() { + request.Header.Set(headerKey, headerValue) + } + + Logger.Printf( + "Sending %s event [%s] to %s project: %d\n", + event.Level, + event.EventID, + t.dsn.host, + t.dsn.projectID, + ) + + response, err := t.client.Do(request) + + if err != nil { + Logger.Printf("There was an issue with sending an event: %v", err) + } + + if response != nil && response.StatusCode == http.StatusTooManyRequests { + t.disabledUntil = time.Now().Add(retryAfter(time.Now(), response)) + Logger.Printf("Too many requests, backing off till: %s\n", t.disabledUntil) + } +} + +// Flush notifies when all the buffered events have been sent by returning `true` +// or `false` if timeout was reached. No-op for HTTPSyncTransport. +func (t *HTTPSyncTransport) Flush(_ time.Duration) bool { + return true +} + +// ================================ +// noopTransport +// ================================ + +// noopTransport is an implementation of `Transport` interface which drops all the events. +// Only used internally when an empty DSN is provided, which effectively disables the SDK. +type noopTransport struct{} + +func (t *noopTransport) Configure(options ClientOptions) { + Logger.Println("Sentry client initialized with an empty DSN. Using noopTransport. No events will be delivered.") +} + +func (t *noopTransport) SendEvent(event *Event) { + Logger.Println("Event dropped due to noopTransport usage.") +} + +func (t *noopTransport) Flush(_ time.Duration) bool { + return true +} diff --git a/vendor/github.com/getsentry/sentry-go/util.go b/vendor/github.com/getsentry/sentry-go/util.go new file mode 100644 index 0000000..6fd6771 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/util.go @@ -0,0 +1,34 @@ +package sentry + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "os" +) + +func uuid() string { + id := make([]byte, 16) + _, _ = io.ReadFull(rand.Reader, id) + id[6] &= 0x0F // clear version + id[6] |= 0x40 // set version to 4 (random uuid) + id[8] &= 0x3F // clear variant + id[8] |= 0x80 // set to IETF variant + return hex.EncodeToString(id) +} + +func fileExists(fileName string) bool { + if _, err := os.Stat(fileName); err != nil { + return false + } + + return true +} + +// nolint: deadcode, unused +func prettyPrint(data interface{}) { + dbg, _ := json.MarshalIndent(data, "", " ") + fmt.Println(string(dbg)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 60fbfec..6f69179 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,3 @@ -# github.com/airbrake/gobrake v3.7.4+incompatible -github.com/airbrake/gobrake -github.com/airbrake/gobrake/internal/lrucache # github.com/aws/aws-sdk-go v1.25.14 github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/awserr @@ -61,14 +58,14 @@ github.com/aws/aws-sdk-go/service/sts github.com/aws/aws-sdk-go/service/sts/stsiface github.com/aws/aws-sdk-go/service/support github.com/aws/aws-sdk-go/service/support/supportiface -# github.com/caio/go-tdigest v2.3.0+incompatible -github.com/caio/go-tdigest # github.com/chanzuckerberg/go-misc v0.0.0-20191016143922-52a18771c2dc github.com/chanzuckerberg/go-misc/aws # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/fatih/color v1.7.0 github.com/fatih/color +# github.com/getsentry/sentry-go v0.3.0 +github.com/getsentry/sentry-go # github.com/google/go-querystring v1.0.0 github.com/google/go-querystring/query # github.com/google/uuid v1.1.1