diff --git a/cmd/api/api.go b/cmd/api/api.go index 984f7e9..b41e88b 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -5,7 +5,9 @@ import ( "os" "os/signal" "syscall" + "time" + "github.com/getsentry/sentry-go" "github.com/gustavobelfort/42-jitsi/internal/config" "github.com/gustavobelfort/42-jitsi/internal/consumers" "github.com/gustavobelfort/42-jitsi/internal/consumers/router" @@ -19,14 +21,18 @@ import ( func init() { config.AddRequired("intra.app_id", "intra.app_secret", "intra.webhooks") if err := config.Initiate(); err != nil { - logrus.Fatalf("could not load configuration: %v", err) + logrus.WithError(err).Fatalf("could not load configuration: %v", err) } + logging.Initiate() if err := db.Init(); err != nil { - logrus.Fatalf("could not connect to the db: %v", err) + logrus.WithError(err).Fatalf("could not connect to the db: %v", err) } } func main() { + if hub := sentry.CurrentHub(); hub.Client() != nil { + defer hub.Flush(time.Second * 5) + } server := &http.Server{ Addr: config.Conf.HTTPAddr, ReadTimeout: config.Conf.Timeout * 2, @@ -36,7 +42,7 @@ func main() { client, err := intra.NewClient(config.Conf.Intra.AppID, config.Conf.Intra.AppSecret, http.DefaultClient) if err != nil { - logrus.Fatalf("could not initiate intra api client: %v", err) + logrus.WithError(err).Fatalf("could not initiate intra api client: %v", err) } hdl := handler.NewScaleTeamHandler(client, db.GlobalDB) @@ -58,7 +64,7 @@ func waitForShutdown(consumer consumers.Consumer) { logrus.Info(err) return } - logrus.Error(err) + logrus.WithError(err).Error(err) } }() <-interruptChan diff --git a/cmd/rabbit/rabbit.go b/cmd/rabbit/rabbit.go index 65f9e0f..42c9dbe 100644 --- a/cmd/rabbit/rabbit.go +++ b/cmd/rabbit/rabbit.go @@ -5,7 +5,9 @@ import ( "os" "os/signal" "syscall" + "time" + "github.com/getsentry/sentry-go" "github.com/gustavobelfort/42-jitsi/internal/config" "github.com/gustavobelfort/42-jitsi/internal/consumers" amqp2 "github.com/gustavobelfort/42-jitsi/internal/consumers/amqp" @@ -21,12 +23,16 @@ func init() { if err := config.Initiate(); err != nil { logrus.Fatalf("could not load configuration: %v", err) } + logging.Initiate() if err := db.Init(); err != nil { logrus.Fatalf("could not connect to the db: %v", err) } } func main() { + if hub := sentry.CurrentHub(); hub.Client() != nil { + defer hub.Flush(time.Second * 5) + } client, err := intra.NewClient(config.Conf.Intra.AppID, config.Conf.Intra.AppSecret, http.DefaultClient) if err != nil { logrus.Fatalf("could not initiate intra api client: %v", err) diff --git a/configs/configs.sample.yml b/configs/configs.sample.yml index 637d99a..6be1e7f 100644 --- a/configs/configs.sample.yml +++ b/configs/configs.sample.yml @@ -1,3 +1,33 @@ +## +# General configuration (mostly used by logs) +## +environment: development +service: 42-jitsi + +## +# Log configuration +## +log_level: debug +# -- sentry configuration +sentry: + dsn: https://identifier@sentry.com/projectid + levels: + - error + - fatal + - panic + enabled: no +# -- logstash configuration +logstash: + host: localhost + port: 5000 + protocol: tcp + levels: + - info + - warning + - error + - fatal + - panic + ## # SlackThat configuration ## diff --git a/configs/example.env b/configs/example.env index e6b87fa..cc6aab3 100644 --- a/configs/example.env +++ b/configs/example.env @@ -1,4 +1,22 @@ ## +# General configuration (mostly used by logs) +## +ENVIRONMENT=development +SERVICE=42-jitsi + +## +# Log configuration +## +LOG_LEVEL=debug +# -- sentry configuration +SENTRY_DSN=https://identifier@sentry.com/projectid +SENTRY_LEVELS=error,fatal,panic +SENTRY_ENABLED=false +# -- logstash configuration +LOGSTASH_HOST=localhost +LOGSTASH_PORT=5000 +LOGSTASH_PROTOCOL=tcp +LOGSTASH_LEVELS=info,warning,error,fatal,panic # Docker Configuration ## JITSI42_CONSUMER_TYPE=api diff --git a/go.mod b/go.mod index 546a36a..4472a16 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,20 @@ go 1.13 require ( github.com/DATA-DOG/go-sqlmock v1.4.1 + github.com/bshuster-repo/logrus-logstash-hook v0.0.0-20190911164539-b3d898b5138a + github.com/getsentry/sentry-go v0.6.0 github.com/gin-gonic/gin v1.6.2 github.com/golang/protobuf v1.4.0 // indirect github.com/jinzhu/gorm v1.9.12 - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lib/pq v1.3.0 // indirect github.com/magiconair/properties v1.8.1 github.com/mitchellh/mapstructure v1.1.2 - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/sirupsen/logrus v1.5.0 + github.com/pkg/errors v0.9.1 // indirect + github.com/sirupsen/logrus v1.6.0 github.com/spf13/viper v1.6.2 github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 github.com/stretchr/testify v1.4.0 - golang.org/x/net v0.0.0-20190620200207-3b0461eec859 + golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be - golang.org/x/sys v0.0.0-20200428200454-593003d681fa // indirect + golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect ) diff --git a/go.sum b/go.sum index a362375..f420ce2 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,70 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bshuster-repo/logrus-logstash-hook v0.0.0-20190911164539-b3d898b5138a h1:8sm9A8pin8aj3hYkQusQQxCJHIBNQsQ0dFKNTZ2MGmM= +github.com/bshuster-repo/logrus-logstash-hook v0.0.0-20190911164539-b3d898b5138a/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 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= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +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/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/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/getsentry/sentry-go v0.6.0 h1:kPd+nr+dlXmaarUBg7xlC/qn+7wyMJL6PMsSn5fA+RM= +github.com/getsentry/sentry-go v0.6.0/go.mod h1:0yZBuzSvbZwBnvaF9VwZIMen3kXscY8/uasKtAX1qG8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -46,6 +76,9 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= @@ -64,6 +97,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -71,6 +105,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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= @@ -79,8 +114,16 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -88,36 +131,60 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +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/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= @@ -128,12 +195,25 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -146,9 +226,13 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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 v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -159,10 +243,12 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 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/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 h1:2MR0pKUzlP3SGgj5NYJe/zRYDwOu9ku6YHy+Iw7l5DM= @@ -180,32 +266,52 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +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.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +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-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190327091125-710a502c58a2/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-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -213,15 +319,20 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA= -golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w= +golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -229,8 +340,10 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -251,9 +364,14 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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/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/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/config/config.go b/internal/config/config.go index 6ad6fb0..3fe55b3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,10 +6,15 @@ import ( "reflect" "strings" "time" + + "github.com/sirupsen/logrus" ) // Configuration is the type that will hold the configuration informations type Configuration struct { + Environment string + Service string + EmailSuffix string `mapstructure:"email_suffix"` SlackThat SlackThatConfig `mapstructure:"slack_that"` WarnBefore time.Duration `mapstructure:"warn_before"` @@ -21,6 +26,26 @@ type Configuration struct { Timeout time.Duration HTTPAddr string `mapstructure:"http_addr"` + + LogLevel logrus.Level `mapstructure:"log_level"` + Logstash Logstash + Sentry Sentry +} + +// Logstash is the type that will hold the logstash informations +type Logstash struct { + Host string + Port string + Protocol string + Levels []logrus.Level + Enabled bool +} + +// Sentry is the type that will hold the sentry informations +type Sentry struct { + DSN string + Levels []logrus.Level + Enabled bool } // Database is the type that will hold the database informations @@ -78,3 +103,10 @@ func stringToMapstringHookFunc(f, t reflect.Type, data interface{}) (interface{} return mapstring, nil } + +func stringToLogLevelHookFunc(f, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t != reflect.TypeOf(logrus.InfoLevel) { + return data, nil + } + return logrus.ParseLevel(data.(string)) +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 7b3ac24..72cdd13 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1,12 +1,12 @@ package config import ( - "fmt" "os" "reflect" "testing" "time" + "github.com/sirupsen/logrus" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,6 +27,26 @@ func TestRequiredConfigError(t *testing.T) { } +func TestStringToLogLevelFuncHook(t *testing.T) { + + t.Run("BadFormat", func(t *testing.T) { + str := "infooooo" + expected := logrus.PanicLevel + level, err := stringToLogLevelHookFunc(reflect.TypeOf(str), reflect.TypeOf(expected), str) + require.Error(t, err) + assert.Equal(t, expected, level) + }) + + t.Run("GoodFormat", func(t *testing.T) { + str := "info" + expected := logrus.InfoLevel + level, err := stringToLogLevelHookFunc(reflect.TypeOf(str), reflect.TypeOf(expected), str) + require.NoError(t, err) + assert.Equal(t, expected, level) + }) + +} + func TestStringToMapstringFuncHook(t *testing.T) { t.Run("BadFormat", func(t *testing.T) { @@ -37,7 +57,7 @@ func TestStringToMapstringFuncHook(t *testing.T) { assert.Equal(t, "expected string of format 'key0:value0,key1:value1,...,keyN:valueN'", err.Error()) }) - t.Run("BadFormat", func(t *testing.T) { + t.Run("GoodFormat", func(t *testing.T) { str := "key:value,key2:value2" expected := map[string]string{"key": "value", "key2": "value2"} mapstring, err := stringToMapstringHookFunc(reflect.TypeOf(str), reflect.MapOf(reflect.TypeOf(str), reflect.TypeOf(str)), str) @@ -49,7 +69,7 @@ func TestStringToMapstringFuncHook(t *testing.T) { func TestInitiate(t *testing.T) { t.Run("NoConfigFile", func(t *testing.T) { - os.Unsetenv("CONFIG_FILE") + os.Setenv("CONFIG_FILE", "") // Setting required fields os.Setenv("INTRA_APP_ID", "intra_app_id") @@ -65,6 +85,9 @@ func TestInitiate(t *testing.T) { defer os.Unsetenv("POSTGRES_PASSWORD") expected := Configuration{ + Environment: "development", + Service: "42-jitsi", + SlackThat: SlackThatConfig{ URL: "http://localhost:8080", Workspace: "42born2code", @@ -81,6 +104,19 @@ func TestInitiate(t *testing.T) { "key": "value", }, }, + LogLevel: logrus.DebugLevel, + Logstash: Logstash{ + Host: "localhost", + Port: "5000", + Protocol: "tcp", + Levels: []logrus.Level{logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}, + Enabled: false, + }, + Sentry: Sentry{ + DSN: "", + Levels: []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}, + Enabled: false, + }, Postgres: Database{ Host: "localhost", Port: "5432", @@ -112,6 +148,9 @@ func TestInitiate(t *testing.T) { os.Setenv("CONFIG_FILE", "../../configs/configs.sample.yml") expected := Configuration{ + Environment: "development", + Service: "42-jitsi", + SlackThat: SlackThatConfig{ URL: "http://localhost:8080", Workspace: "42born2code", @@ -128,6 +167,19 @@ func TestInitiate(t *testing.T) { "--FILL": "ME--", }, }, + LogLevel: logrus.DebugLevel, + Logstash: Logstash{ + Host: "localhost", + Port: "5000", + Protocol: "tcp", + Levels: []logrus.Level{logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}, + Enabled: false, + }, + Sentry: Sentry{ + DSN: "https://identifier@sentry.com/projectid", + Levels: []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}, + Enabled: false, + }, Postgres: Database{ Host: "localhost", Port: "5432", @@ -146,7 +198,6 @@ func TestInitiate(t *testing.T) { } assert.NoError(t, Initiate()) - fmt.Println(viper.GetString("postgres.password")) assert.Equal(t, expected, Conf) }) } diff --git a/internal/config/load.go b/internal/config/load.go index 82cf2f5..5d3afbf 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -1,10 +1,8 @@ package config import ( - "errors" "time" - "github.com/gustavobelfort/42-jitsi/internal/logging" "github.com/mitchellh/mapstructure" log "github.com/sirupsen/logrus" "github.com/spf13/viper" @@ -27,6 +25,9 @@ func setDefaults() { viper.SetDefault("config_file", "./config.yml") + viper.SetDefault("environment", "development") + viper.SetDefault("service", "42-jitsi") + viper.SetDefault("email_suffix", "student.42campus.org") viper.SetDefault("warn_before", time.Minute*15) @@ -49,6 +50,17 @@ func setDefaults() { viper.SetDefault("rabbitmq.user", "guest") viper.SetDefault("rabbitmq.password", "guest") viper.SetDefault("rabbitmq.queue", "webhooks_intra_42jitsi") + + viper.SetDefault("log_level", log.DebugLevel) + + viper.SetDefault("logstash.host", "localhost") + viper.SetDefault("logstash.port", "5000") + viper.SetDefault("logstash.protocol", "tcp") + viper.SetDefault("logstash.levels", []log.Level{log.InfoLevel, log.WarnLevel, log.ErrorLevel, log.FatalLevel, log.PanicLevel}) + viper.SetDefault("logstash.enabled", false) + + viper.SetDefault("sentry.levels", []log.Level{log.ErrorLevel, log.FatalLevel, log.PanicLevel}) + viper.SetDefault("sentry.enabled", false) } func bindEnv() { @@ -65,6 +77,8 @@ func bindEnv() { log.Debugf("bound '%s' to '%s'", env, config) } + logBinding("sentry.dsn", "SENTRY_DSN") + logBinding("postgres.password", "POSTGRES_PASSWORD") logBinding("intra.app_id", "INTRA_APP_ID") logBinding("intra.app_secret", "INTRA_APP_SECRET") @@ -74,24 +88,26 @@ func bindEnv() { } -func loadFile() error { +func loadFile() { viper.SetConfigType("yaml") filename := viper.GetString("config_file") if filename == "" { - return logging.WithLog(errors.New("'config_file' is empty"), log.InfoLevel, nil) + log.Info("'config_file' is empty") + return } viper.SetConfigFile(viper.GetString("config_file")) if err := viper.ReadInConfig(); err != nil { - return logging.WithLog(err, log.ErrorLevel, log.Fields{"config_file": filename}) + log.WithField("config_file", filename).WithError(err).Errorf("loading config file: %v", err) + return } - log.WithField("config_file", filename).Infof("loaded config from: '%s'\n", filename) - return nil + log.WithField("config_file", filename).Infof("loaded config from: '%s'", filename) } func unmarshalConfig() error { decodeHook := mapstructure.ComposeDecodeHookFunc( stringToMapstringHookFunc, + stringToLogLevelHookFunc, mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ) @@ -103,9 +119,7 @@ func Initiate() error { log.Infoln("loading configuration") setDefaults() bindEnv() - err := loadFile() - // logging.LogError won't log if err is nil. - logging.LogError(log.StandardLogger(), err, "loading config file") + loadFile() if err := checkRequired(requiredConf...); err != nil { return err diff --git a/internal/logging/context.go b/internal/logging/context.go index abfe4e6..401662f 100644 --- a/internal/logging/context.go +++ b/internal/logging/context.go @@ -11,6 +11,7 @@ type contextKey int const ( logFieldsKey contextKey = iota + sentryCategoryKey ) // ContextLog returns an entry with the fields of the passed context. @@ -42,3 +43,17 @@ func ContextWithFields(ctx context.Context, fields logrus.Fields) context.Contex func ContextWithField(ctx context.Context, key string, value interface{}) context.Context { return ContextWithFields(ctx, logrus.Fields{key: value}) } + +// ContextWithSentryCategory sets the sentry category for the given context. +func ContextWithSentryCategory(ctx context.Context, category string) context.Context { + return context.WithValue(ctx, sentryCategoryKey, category) +} + +// ContextGetSentryCategory gets the sentry category of the given context. +func ContextGetSentryCategory(ctx context.Context) string { + category := "" + if ctx != nil { + category, _ = ctx.Value(sentryCategoryKey).(string) + } + return category +} diff --git a/internal/logging/context_test.go b/internal/logging/context_test.go index 0ad0a3c..6e573cb 100644 --- a/internal/logging/context_test.go +++ b/internal/logging/context_test.go @@ -62,3 +62,41 @@ func TestContextLog(t *testing.T) { assert.Equal(t, expected, entry.Data) } + +func TestContextGetSentryCategory(t *testing.T) { + t.Run("NilContext", func(t *testing.T) { + assert.Equal(t, "", ContextGetSentryCategory(nil)) + }) + + t.Run("EmptyContext", func(t *testing.T) { + assert.Equal(t, "", ContextGetSentryCategory(context.Background())) + }) + + t.Run("WithCategory", func(t *testing.T) { + expected := "category" + + ctx := context.WithValue(context.Background(), sentryCategoryKey, expected) + + assert.Equal(t, expected, ContextGetSentryCategory(ctx)) + }) +} + +func TestContextWithSentryCategory(t *testing.T) { + ctx := context.Background() + + t.Run("SetCategory", func(t *testing.T) { + expected := "category" + + ctx = ContextWithSentryCategory(ctx, expected) + + assert.Equal(t, expected, ctx.Value(sentryCategoryKey)) + }) + + t.Run("OverwriteCategory", func(t *testing.T) { + expected := "other_category" + + ctx = ContextWithSentryCategory(ctx, expected) + + assert.Equal(t, expected, ctx.Value(sentryCategoryKey)) + }) +} diff --git a/internal/logging/error_wrapper.go b/internal/logging/error_wrapper.go index 0ce35c5..edf2db1 100644 --- a/internal/logging/error_wrapper.go +++ b/internal/logging/error_wrapper.go @@ -28,7 +28,7 @@ func LogError(logger logrus.FieldLogger, err error, message string) { logLevel = logErr.LogLevel logFields = logErr.ExtraFields } - logger.WithFields(logFields).WithField("error", err.Error()).Logf(logLevel, "%s: %v\n", message, err) + logger.WithFields(logFields).WithError(err).Logf(logLevel, "%s: %v\n", message, err) if logLevel == logrus.FatalLevel { logrus.Exit(1) } diff --git a/internal/logging/error_wrapper_test.go b/internal/logging/error_wrapper_test.go index ca87ca4..3815ab9 100644 --- a/internal/logging/error_wrapper_test.go +++ b/internal/logging/error_wrapper_test.go @@ -18,7 +18,7 @@ func TestLogError(t *testing.T) { expectedMessage := "message" expectedLogLevel := logrus.ErrorLevel expectedFields := logrus.Fields{ - "error": expectedError.Error(), + "error": expectedError, } assertExpectations := func(t *testing.T) { @@ -54,14 +54,14 @@ func TestLogError(t *testing.T) { expectedLogLevel = logrus.WarnLevel expectedFields = logrus.Fields{ - "error": expectedError.Error(), - "key": "value", + "key": "value", } expectedError = &WithLogError{ LogLevel: expectedLogLevel, ExtraFields: expectedFields, err: expectedError, } + expectedFields["error"] = expectedError LogError(logger, expectedError, expectedMessage) assertExpectations(t) diff --git a/internal/logging/init.go b/internal/logging/init.go new file mode 100644 index 0000000..6e17f81 --- /dev/null +++ b/internal/logging/init.go @@ -0,0 +1,51 @@ +package logging + +import ( + "fmt" + + "github.com/getsentry/sentry-go" + "github.com/gustavobelfort/42-jitsi/internal/config" + "github.com/sirupsen/logrus" +) + +// InitiateSentry will initiate sentry logging on the standard logger based on the app's configuration. +func InitiateSentry() error { + if !config.Conf.Sentry.Enabled { + logrus.Debug("sentry is disabled by config") + return nil + } + logrus.Info("initiating sentry") + options := sentry.ClientOptions{ + Dsn: config.Conf.Sentry.DSN, + AttachStacktrace: true, + Environment: config.Conf.Environment, + MaxBreadcrumbs: 100, + } + if err := sentry.Init(options); err != nil { + return err + } + AddSentryHook(sentry.CurrentHub(), logrus.StandardLogger(), config.Conf.Sentry.Levels) + return nil +} + +// InitiateLogstash will initiate logstash logging on the standard logger based on the app's configuration. +func InitiateLogstash() error { + if !config.Conf.Logstash.Enabled { + logrus.Debug("logstash is disabled by config") + return nil + } + logrus.Info("initiating logstash") + addr := fmt.Sprintf("%s:%s", config.Conf.Logstash.Host, config.Conf.Logstash.Port) + fields := logrus.Fields{ + "environment": config.Conf.Environment, + "service": config.Conf.Service, + } + return AddLogstashHook(config.Conf.Logstash.Protocol, addr, logrus.StandardLogger(), fields, config.Conf.Logstash.Levels) +} + +// Initiate will initiate logging based on the app's configuration. +func Initiate() { + logrus.SetLevel(config.Conf.LogLevel) + LogError(logrus.StandardLogger(), InitiateSentry(), "while initiating sentry") + LogError(logrus.StandardLogger(), InitiateLogstash(), "while initiating logstash") +} diff --git a/internal/logging/logstash.go b/internal/logging/logstash.go new file mode 100644 index 0000000..e6a8c04 --- /dev/null +++ b/internal/logging/logstash.go @@ -0,0 +1,33 @@ +package logging + +import ( + "net" + + logrustash "github.com/bshuster-repo/logrus-logstash-hook" + "github.com/sirupsen/logrus" +) + +// LogstashHook wraps the base logstash hook to enable level configuration. +type LogstashHook struct { + logrus.Hook + levels []logrus.Level +} + +// Levels return the configured levels of the logstash hook wrapper. +func (hook *LogstashHook) Levels() []logrus.Level { + return hook.levels +} + +// AddLogstashHook adds a logstash hook to the given logger. +func AddLogstashHook(protocol, address string, logger *logrus.Logger, fields logrus.Fields, levels []logrus.Level) error { + conn, err := net.Dial(protocol, address) + if err != nil { + return err + } + hook := &LogstashHook{ + Hook: logrustash.New(conn, logrustash.DefaultFormatter(fields)), + levels: levels, + } + logger.AddHook(hook) + return nil +} diff --git a/internal/logging/sentry.go b/internal/logging/sentry.go new file mode 100644 index 0000000..869dae2 --- /dev/null +++ b/internal/logging/sentry.go @@ -0,0 +1,91 @@ +package logging + +import ( + "errors" + "fmt" + "time" + + "github.com/getsentry/sentry-go" + "github.com/sirupsen/logrus" +) + +// sentryHub narrows down the sentry.Hub structure methods to the methods needed by SentryHook. +type sentryHub interface { + AddBreadcrumb(breadcrumb *sentry.Breadcrumb, hint *sentry.BreadcrumbHint) + CaptureException(exception error) *sentry.EventID + Flush(timeout time.Duration) bool +} + +// SentryHook is a custom logrus hook that works with the latest sentry package for go. +type SentryHook struct { + hub sentryHub + errorLevels map[logrus.Level]interface{} +} + +func (hook *SentryHook) handleError(entry *logrus.Entry) { + err := errors.New(entry.Message) + + if _, ok := entry.Data["error"]; ok { + if msgErr, ok := entry.Data["error"].(error); ok && msgErr != nil { + err = msgErr + } else if msgErr, ok := entry.Data["error"].(string); ok && msgErr != "" { + err = errors.New(msgErr) + } else if msgErr, ok := entry.Data["error"].(fmt.Stringer); ok && msgErr != nil { + err = errors.New(msgErr.String()) + } + } + + hook.hub.CaptureException(err) +} + +func (hook *SentryHook) handleBreadcrumb(entry *logrus.Entry, breadcrumbType string) { + category := ContextGetSentryCategory(entry.Context) + if category == "" { + category = "default" + } + breadcrumb := &sentry.Breadcrumb{ + Category: category, + Data: entry.Data, + Level: sentry.Level(entry.Level.String()), + Message: entry.Message, + Timestamp: entry.Time, + Type: breadcrumbType, + } + hook.hub.AddBreadcrumb(breadcrumb, nil) +} + +// Fire fire the logrus hook. It adds the current logging as a breadcrumb. And creates an event if the entry's level +// is part of the previously set error levels. +func (hook *SentryHook) Fire(entry *logrus.Entry) error { + if _, ok := hook.errorLevels[entry.Level]; ok { + hook.handleBreadcrumb(entry, "error") + hook.handleError(entry) + return nil + } + hook.handleBreadcrumb(entry, "default") + return nil +} + +func (hook *SentryHook) addErrorLevels(levels []logrus.Level) { + if hook.errorLevels == nil { + hook.errorLevels = make(map[logrus.Level]interface{}) + } + for _, level := range levels { + hook.errorLevels[level] = struct{}{} + } +} + +// Levels returns all the levels as all logs should be passed as breadcrumb. +func (hook *SentryHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +// AddSentryHook adds a sentry hook to the given logger. +func AddSentryHook(hub *sentry.Hub, logger *logrus.Logger, levels []logrus.Level) { + hook := &SentryHook{ + hub: hub, + } + hook.addErrorLevels(levels) + logger.AddHook(hook) + logrus.DeferExitHandler(func() { hub.Flush(time.Second * 5) }) +} diff --git a/internal/logging/sentry_test.go b/internal/logging/sentry_test.go new file mode 100644 index 0000000..69a7228 --- /dev/null +++ b/internal/logging/sentry_test.go @@ -0,0 +1,195 @@ +package logging + +import ( + "context" + "errors" + "io/ioutil" + "testing" + "time" + + "github.com/getsentry/sentry-go" + "github.com/magiconair/properties/assert" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type HubMock struct { + mock.Mock +} + +func (m *HubMock) AddBreadcrumb(breadcrumb *sentry.Breadcrumb, _ *sentry.BreadcrumbHint) { + m.Called(breadcrumb.Level, breadcrumb.Message, logrus.Fields(breadcrumb.Data), breadcrumb.Category, breadcrumb.Type) +} + +func (m *HubMock) CaptureException(exception error) *sentry.EventID { + return m.Called(exception).Get(0).(*sentry.EventID) +} + +func (m *HubMock) Flush(timeout time.Duration) bool { + return m.Called(timeout).Bool(0) +} + +func TestSentryHook(t *testing.T) { + suite.Run(t, new(SentryHookSuite)) +} + +type SentryHookSuite struct { + suite.Suite + + mock *HubMock + + logger *logrus.Logger + testHook *test.Hook + + sentryHook *SentryHook +} + +func (s *SentryHookSuite) SetupSuite() { + s.mock = &HubMock{} + + s.logger, s.testHook = test.NewNullLogger() + + s.sentryHook = &SentryHook{hub: s.mock} + s.Equal(logrus.AllLevels, s.sentryHook.Levels()) + s.logger.AddHook(s.sentryHook) + s.logger.SetLevel(logrus.DebugLevel) +} + +func (s *SentryHookSuite) SetupTest() { + s.mock.Calls = []mock.Call{} + s.mock.ExpectedCalls = []*mock.Call{} + + s.testHook.Reset() +} + +func (s *SentryHookSuite) Test00_addErrorLevels() { + expected := map[logrus.Level]interface{}{ + logrus.ErrorLevel: struct{}{}, + logrus.FatalLevel: struct{}{}, + } + s.sentryHook.addErrorLevels([]logrus.Level{logrus.ErrorLevel, logrus.FatalLevel}) + s.Equal(expected, s.sentryHook.errorLevels) +} + +func (s *SentryHookSuite) Test01_InfoLevel() { + expectedLevel := sentry.LevelInfo + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data"} + expectedCategory := "default" + expectedType := "default" + + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + s.logger.WithFields(expectedFields).Info(expectedMessage) +} + +func (s *SentryHookSuite) Test02_DebugLevel_ChangeCategory() { + expectedLevel := sentry.LevelDebug + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data"} + expectedCategory := "testing" + expectedType := "default" + + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + ctx := ContextWithSentryCategory(context.Background(), expectedCategory) + s.logger.WithContext(ctx).WithFields(expectedFields).Debug(expectedMessage) +} + +func (s *SentryHookSuite) Test03_ErrorLevel() { + expectedError := errors.New("testing error") + expectedLevel := sentry.LevelError + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data", "error": expectedError} + expectedCategory := "default" + expectedType := "error" + + expectedID := "id" + + s.mock.On("CaptureException", expectedError).Return((*sentry.EventID)(&expectedID)) + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + s.logger.WithFields(expectedFields).WithError(expectedError).Error(expectedMessage) +} + +func (s *SentryHookSuite) Test04_ErrorLevel_StringError() { + expectedError := errors.New("testing error") + expectedLevel := sentry.LevelError + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data", "error": expectedError.Error()} + expectedCategory := "default" + expectedType := "error" + + expectedID := "id" + + s.mock.On("CaptureException", expectedError).Return((*sentry.EventID)(&expectedID)) + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + s.logger.WithFields(expectedFields).Error(expectedMessage) +} + +type stringer struct{} + +func (*stringer) String() string { + return "testing error" +} + +func (s *SentryHookSuite) Test05_ErrorLevel_StringerError() { + expectedError := errors.New("testing error") + expectedLevel := sentry.LevelError + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data", "error": &stringer{}} + expectedCategory := "default" + expectedType := "error" + + expectedID := "id" + + s.mock.On("CaptureException", expectedError).Return((*sentry.EventID)(&expectedID)) + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + s.logger.WithFields(expectedFields).Error(expectedMessage) +} + +func (s *SentryHookSuite) Test06_ErrorLevel_NoError() { + expectedLevel := sentry.LevelError + expectedMessage := "testing message" + expectedFields := logrus.Fields{"testing": "data"} + expectedCategory := "default" + expectedType := "error" + expectedError := errors.New(expectedMessage) + + expectedID := "id" + + s.mock.On("CaptureException", expectedError).Return((*sentry.EventID)(&expectedID)) + s.mock.On("AddBreadcrumb", expectedLevel, expectedMessage, expectedFields, expectedCategory, expectedType).Return().Once() + + s.logger.WithFields(expectedFields).Error(expectedMessage) +} + +func (s *SentryHookSuite) TearDownTest() { + s.mock.AssertExpectations(s.T()) +} + +func TestAddSentryHook(t *testing.T) { + logger := logrus.New() + logger.SetOutput(ioutil.Discard) + + expectedHub := &sentry.Hub{} + expectedErrorLevels := map[logrus.Level]interface{}{ + logrus.ErrorLevel: struct{}{}, + logrus.FatalLevel: struct{}{}, + } + + AddSentryHook(expectedHub, logger, []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel}) + + for _, hooks := range logger.Hooks { + require.IsType(t, &SentryHook{}, hooks[0]) + } + hook := logger.Hooks[logrus.ErrorLevel][0].(*SentryHook) + + assert.Equal(t, expectedHub, hook.hub) + assert.Equal(t, expectedErrorLevels, hook.errorLevels) +}