diff --git a/api/router/correlation_test.go b/api/router/correlation_test.go new file mode 100644 index 00000000..6cd10e32 --- /dev/null +++ b/api/router/correlation_test.go @@ -0,0 +1,85 @@ +package router_test + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/allinbits/demeris-api-server/api/config" + "github.com/allinbits/demeris-api-server/api/database" + "github.com/allinbits/demeris-api-server/api/router" + "github.com/allinbits/emeris-utils/logging" + "github.com/allinbits/emeris-utils/store" + "github.com/cockroachdb/cockroach-go/v2/testserver" + "github.com/gofrs/uuid" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +func TestCorrelationIDMiddleWare(t *testing.T) { + t.Parallel() + r, cfg, observedLogs, tDown := setup(t) + defer tDown() + require.NotNil(t, r) + + go r.Serve(cfg.ListenAddr) + time.Sleep(2 * time.Second) + + client := http.Client{ + Timeout: 2 * time.Second, + } + req, err := http.NewRequest("GET", fmt.Sprintf("http://%s%s", cfg.ListenAddr, "/chains"), nil) + require.NoError(t, err) + + id, err := uuid.NewV4() + require.NoError(t, err) + + req.Header.Set("X-Correlation-id", fmt.Sprintf("%x", id)) + + _, err = client.Do(req) + require.NoError(t, err) + + require.Eventually(t, func() bool { + count := 0 + for _, info := range observedLogs.All() { + if info.ContextMap()[string(logging.IntCorrelationIDName)] != nil { + count++ + } + if info.ContextMap()[string(logging.CorrelationIDName)] == fmt.Sprintf("%x", id) { + count++ + } + } + return count == 2 + }, 5*time.Second, 1*time.Second) +} + +func setup(t *testing.T) (router.Router, config.Config, *observer.ObservedLogs, func()) { + tServer, err := testserver.NewTestServer() + require.NoError(t, err) + + require.NoError(t, tServer.WaitForInit()) + + connStr := tServer.PGURL().String() + require.NotNil(t, connStr) + + cfg := &config.Config{ + DatabaseConnectionURL: connStr, + ListenAddr: "127.0.0.1:9090", + RedisAddr: "127.0.0.1:6379", + KubernetesNamespace: "emeris", + Debug: true, + } + + db, err := database.Init(cfg) + require.NoError(t, err) + + s, err := store.NewClient(cfg.RedisAddr) + require.NoError(t, err) + + observedZapCore, observedLogs := observer.New(zap.InfoLevel) + observedLogger := zap.New(observedZapCore) + + return *router.New(db, observedLogger.Sugar(), s, nil, "", nil, cfg.Debug), *cfg, observedLogs, func() { tServer.Stop() } +} diff --git a/api/router/deps/deps.go b/api/router/deps/deps.go index d9e6aeea..3cbddded 100644 --- a/api/router/deps/deps.go +++ b/api/router/deps/deps.go @@ -7,6 +7,7 @@ import ( kube "sigs.k8s.io/controller-runtime/pkg/client" "github.com/allinbits/demeris-api-server/api/database" + "github.com/allinbits/emeris-utils/logging" "github.com/allinbits/emeris-utils/store" "github.com/gin-gonic/gin" "go.uber.org/zap" @@ -38,9 +39,17 @@ func GetDeps(c *gin.Context) *Deps { // WriteError lgos and return client-facing errors func (d *Deps) WriteError(c *gin.Context, err Error, logMessage string, keyAndValues ...interface{}) { + + // setting error id + value, ok := c.Request.Context().Value(logging.IntCorrelationIDName).(string) + if !ok { + panic("cant get value int_correlation_id") + } + err.ID = value _ = c.Error(err) if keyAndValues != nil { + keyAndValues = append(keyAndValues, "error", err) d.Logger.Errorw( logMessage, keyAndValues..., diff --git a/api/router/deps/error.go b/api/router/deps/error.go index 9c1627b5..93dec906 100644 --- a/api/router/deps/error.go +++ b/api/router/deps/error.go @@ -2,21 +2,8 @@ package deps import ( "fmt" - "strconv" - "sync" - - "github.com/sony/sonyflake" ) -var flake *sonyflake.Sonyflake -var once sync.Once - -func init() { - once.Do(func() { - flake = sonyflake.NewSonyflake(sonyflake.Settings{}) - }) -} - type Error struct { ID string `json:"id"` Namespace string `json:"namespace"` @@ -34,15 +21,7 @@ func (e Error) Unwrap() error { } func NewError(namespace string, cause error, statusCode int) Error { - id, err := flake.NextID() - if err != nil { - panic(fmt.Errorf("cannot create sonyflake, %w", err)) - } - - idstr := strconv.FormatUint(id, 10) - return Error{ - ID: idstr, StatusCode: statusCode, Namespace: namespace, LowLevelError: cause, diff --git a/api/router/router.go b/api/router/router.go index 9d5b414b..b9866249 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -8,11 +8,11 @@ import ( "github.com/allinbits/demeris-api-server/api/block" "github.com/allinbits/demeris-api-server/api/cached" "github.com/allinbits/demeris-api-server/api/liquidity" - "github.com/allinbits/emeris-utils/logging" "k8s.io/client-go/informers" "github.com/allinbits/demeris-api-server/api/relayer" + "github.com/allinbits/emeris-utils/logging" "github.com/allinbits/emeris-utils/validation" "github.com/gin-gonic/gin/binding" @@ -57,6 +57,7 @@ func New( engine := gin.New() + engine.Use(logging.CorrelationIDMiddleware(l)) r := &Router{ g: engine, DB: db, @@ -71,10 +72,11 @@ func New( validation.JSONFields(binding.Validator) - engine.Use(r.catchPanicsFunc) if debug { engine.Use(logging.LogRequest(l.Desugar())) } + engine.Use(r.setLoggerFromContext) + engine.Use(r.catchPanicsFunc) engine.Use(r.decorateCtxWithDeps) engine.Use(r.handleErrors) engine.RedirectTrailingSlash = false @@ -89,6 +91,16 @@ func (r *Router) Serve(address string) error { return r.g.Run(address) } +func (r *Router) setLoggerFromContext(c *gin.Context) { + l, err := logging.GetLoggerFromContext(c) + if err != nil && r.l == nil { + panic("cant get logger from context") + } + if l != nil { + r.l = l + } +} + func (r *Router) catchPanicsFunc(c *gin.Context) { defer func() { if rval := recover(); rval != nil { diff --git a/go.mod b/go.mod index 81afabfb..a8f8b100 100644 --- a/go.mod +++ b/go.mod @@ -20,11 +20,11 @@ require ( github.com/cosmos/cosmos-sdk v0.42.8 github.com/gin-gonic/gin v1.7.7 github.com/go-playground/validator/v10 v10.10.0 + github.com/gofrs/uuid v4.2.0+incompatible github.com/google/go-cmp v0.5.7 github.com/gravity-devs/liquidity v1.2.9 github.com/jmoiron/sqlx v1.3.3 github.com/lib/pq v1.10.4 - github.com/sony/sonyflake v1.0.0 github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 github.com/swaggo/swag v1.7.9 github.com/tendermint/tendermint v0.34.11 @@ -87,7 +87,6 @@ require ( github.com/go-redis/redis/v8 v8.11.4 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 28165f10..8022f218 100644 --- a/go.sum +++ b/go.sum @@ -346,8 +346,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= -github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= -github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -1362,8 +1360,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM= -github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=