From a1245faaebe59cb6c4d9bec112507096ab0be533 Mon Sep 17 00:00:00 2001 From: Gleb Nazemnov Date: Tue, 28 Nov 2023 21:56:01 +0100 Subject: [PATCH 1/2] Update ReadMe (#16) Update ReadMe --- README.md | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 662153c..9c7ae0b 100644 --- a/README.md +++ b/README.md @@ -45,52 +45,67 @@ package main import ( "context" + "os" "sync" "time" + "github.com/glebnaz/witcher/db/mongo" "github.com/glebnaz/witcher/engine" + "github.com/glebnaz/witcher/grpc" + wlog "github.com/glebnaz/witcher/log" "github.com/rs/zerolog/log" ) func main() { - log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.TextFormatter{ - DisableColors: false, - }) + wlog.InitLog(os.Stdout, false) + serverGRPC := grpc.NewDefaultServer("witcher_example") + s := engine.NewServer(engine.WithGRPCServer(serverGRPC, ":8082", time.Second*5)) - s := engine.NewServer() + checker1 := engine.NewDefaultChecker("checker false", func() error { + log.Info().Msgf("Checker1 is running") + return nil + }) - checker := engine.NewDefaultChecker("checker", func() error { - log.Infof("Checker is running") + checker2 := engine.NewDefaultChecker("checker true", func() error { + log.Info().Msgf("Checker2 is running") return nil }) closer := engine.NewDefaultCloser("closer", func(ctx context.Context, wg *sync.WaitGroup) error { - log.Infof("Closer is running") + log.Info().Msgf("Closer is running") time.Sleep(time.Second * 10) - log.Infof("Closer is running after 10 seconds") + log.Info().Msgf("Closer is running after 10 seconds") wg.Done() return nil }) - s.AddChecker(checker) + s.AddCheckers([]engine.Checker{checker1, checker2}) s.AddCloser(closer) s.AddActor(func() error { i := 0 for { i++ - log.Debugf("Actor is running %d", i) + log.Debug().Msgf("Actor is running %d", i) time.Sleep(time.Second * 1) } }, func(error) { - log.Errorf("Actor failed") + log.Error().Msg("Actor failed") }) + m, err := mongo.NewMongo(s.GetCTX(), "mongodb://localhost:27017") + if err != nil { + log.Error().Msgf("Error connect to mongo: %s", err) + } + + s.AddCloser(m.Closer()) + s.AddChecker(m.HealthChecker(time.Second * 5)) + if err := s.Run(); err != nil { - log.Errorf("Error Run Server: %s", err) + log.Error().Msgf("Error Run Server: %s", err) } } + ``` ## Debug Server From 66f1952c489bc6f0628969c84c2086ecb8317fb8 Mon Sep 17 00:00:00 2001 From: Gleb Nazemnov Date: Thu, 25 Apr 2024 21:02:14 +0200 Subject: [PATCH 2/2] v0.2.0 (#18) * v0.2.0 Added - Added pprof tests to improve performance profiling. - Improved engine tests for better coverage and reliability. - Added tests for the `add checker` functionality. - Added more tests for the debug server to ensure its robustness. - Added test coverage configuration to maintain code quality. - Added simple test for the debug server to verify its basic functionality. - Added tests for GitHub actions to ensure CI/CD pipeline works as expected. Changed - Updated Go version to 1.21 for compatibility and performance improvements. Fixed - Fixed naming conventions in the codebase for better readability and maintainability. - Fixed Go version in linter configuration to match the project's Go version. - Introduced a new custom error for closers to handle closing operations more effectively. - Fixed issues with the probe for better monitoring and observability. --------- Co-authored-by: Mikhail Barshev <54182699+mike-barshev@users.noreply.github.com> --- .github/workflows/go.yml | 12 +- .golangci.yml | 40 +++- README.md | 6 +- db/mongo/mongo.go | 15 +- db/mongo/mongo_test.go | 172 +++++++++++++++++ engine/checker.go | 18 +- engine/closer.go | 13 +- engine/debug.go | 124 ++++++++---- engine/engine.go | 38 ++-- engine/engine_test.go | 408 +++++++++++++++++++++++++++++++++++++++ engine/pprof.go | 126 ++---------- example/service/main.go | 8 +- go.mod | 67 +++++-- go.sum | 195 +++++++++++++++---- log/test_log.go | 72 +++++++ metrics/http_mw.go | 39 ++-- 16 files changed, 1083 insertions(+), 270 deletions(-) create mode 100644 db/mongo/mongo_test.go create mode 100644 engine/engine_test.go create mode 100644 log/test_log.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d5b9022..869d7a0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,7 +7,6 @@ on: jobs: lint: runs-on: ubuntu-latest - steps: - name: set up go 1.21 uses: actions/setup-go@v2 @@ -22,4 +21,13 @@ jobs: run: | curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $GITHUB_WORKSPACE v1.54.0 - name: run linters - run: $GITHUB_WORKSPACE/golangci-lint run --config .golangci.yml ./... \ No newline at end of file + run: $GITHUB_WORKSPACE/golangci-lint run --config .golangci.yml ./... + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.22 + - name: generate test coverage + run: go test -cover ./... \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index aecd3f6..f6359da 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,19 +29,39 @@ linters-settings: goconst: min-len: 2 min-occurrences: 2 + lll: + # Max line length, lines longer will be reported. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option. + # Default: 120. + line-length: 500 + # Tab width in spaces. + # Default: 1 + tab-width: 2 + misspell: + locale: US linters: disable-all: true enable: + - dupl + - misspell + - unused + - goimports + - goconst - revive - - govet - errcheck - - deadcode - - structcheck - - varcheck - - ineffassign - - typecheck - - goconst - - gosec - - goimports - - exportloopref + - noctx + - nilerr + - paralleltest + - lll + + +issues: + exclude-use-default: false + exclude: + - G104 + - (comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form) + - should have a package comment, unless it's in another file for this package + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv|.*Rollback). is not checked + - should check returned error before deferring + - declaration of "ok" shadows declaration \ No newline at end of file diff --git a/README.md b/README.md index 9c7ae0b..ba4d882 100644 --- a/README.md +++ b/README.md @@ -115,12 +115,14 @@ Your Debug server has three handlers: * /live - live probe -* /ready - ready probe +* /read - readiness probe + +* /startup - startup * /metrics - Prometheus metrics #### PPROF -You can use pprof to see the CPU and memory usage of your application on the Debug server. Go to "/" to see the start page of pprof. +You can use pprof to see the CPU and memory usage of your application on the Debug server. Go to "/debug/pprof/" to see the start page of pprof. By default, the server is listening on port 8084. You can change the port by setting the environment variable `PORT` or by using `WithDebugPort("your_port")`. diff --git a/db/mongo/mongo.go b/db/mongo/mongo.go index b5d283b..5e78d53 100644 --- a/db/mongo/mongo.go +++ b/db/mongo/mongo.go @@ -18,13 +18,15 @@ type Mongo struct { lock sync.RWMutex name string + + once sync.Once } // HealthChecker use for registration mongo health check in wither engine // timeout is duration for ping (use context.WithTimeout inside) func (m *Mongo) HealthChecker(timeout time.Duration) engine.Checker { - return engine.NewDefaultChecker(m.GetName(), func() error { - ctx, cancel := context.WithTimeout(context.Background(), timeout) + return engine.NewDefaultChecker(m.GetName(), func(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, timeout) err := m.Ping(ctx, nil) defer cancel() if err != nil { @@ -35,9 +37,14 @@ func (m *Mongo) HealthChecker(timeout time.Duration) engine.Checker { } // Closer use for registration mongo closer in wither engine +// +// this func allocate new closer for mongo +// USE IT ONLY ONE TIME func (m *Mongo) Closer() engine.Closer { - return engine.NewDefaultCloser(m.GetName(), func(ctx context.Context, group *sync.WaitGroup) error { - defer group.Done() + m.once.Do(func() { + log.Debug().Msgf("Init closer for mongo") + }) + return engine.NewDefaultCloser(m.GetName(), func(ctx context.Context) error { err := m.Disconnect(ctx) if err != nil { log.Debug().Msgf("Error disconnect mongo: %s", err) diff --git a/db/mongo/mongo_test.go b/db/mongo/mongo_test.go new file mode 100644 index 0000000..592af66 --- /dev/null +++ b/db/mongo/mongo_test.go @@ -0,0 +1,172 @@ +package mongo + +import ( + "context" + "testing" + "time" + + "github.com/glebnaz/witcher/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +type MongoTestSuite struct { + suite.Suite + container testcontainers.Container + + endpoint string +} + +const ( + uriProtocol = "mongodb://" +) + +func (m *MongoTestSuite) SetupTest() { + ctx := context.Background() + + req := testcontainers.ContainerRequest{ + Image: "mongo:latest", + ExposedPorts: []string{"27017/tcp"}, + WaitingFor: wait.ForExposedPort(), + } + + l := log.NewTestLogger(log.WithTestLoggerName("mongo_test_suite")) + + mongoContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + Logger: l, + }) + + if err != nil { + m.FailNow(err.Error()) + } + + m.container = mongoContainer + endpoint, err := m.container.Endpoint(ctx, "") + if err != nil { + m.FailNow(err.Error()) + } + m.endpoint = uriProtocol + endpoint +} + +func (m *MongoTestSuite) TearDownTest() { + err := m.container.Terminate(context.Background()) + if err != nil { + m.FailNow(err.Error()) + } +} + +func (m *MongoTestSuite) TestCloser() { + t := m.Suite.T() + + type args struct { + ctx context.Context + cancel context.CancelFunc + uri string + } + + type want struct { + errFunc assert.ErrorAssertionFunc + } + + tests := []struct { + name string + args args + want want + }{ + { + name: "success close", + args: args{ + ctx: context.Background(), + uri: m.endpoint, + }, + want: want{ + errFunc: assert.NoError, + }, + }, + } + + for _, tt := range tests { + m.Run(tt.name, func() { + instance, err := NewMongo(tt.args.ctx, tt.args.uri) + assert.NoError(t, err) + closer := instance.Closer() + + if tt.args.cancel != nil { + tt.args.cancel() + } + + err = closer.Close(tt.args.ctx) + tt.want.errFunc(t, err) + }) + } +} + +func (m *MongoTestSuite) TestHealthChecker() { + t := m.Suite.T() + + type args struct { + ctx context.Context + cancel context.CancelFunc + uri string + } + + type want struct { + errFunc assert.ErrorAssertionFunc + } + + ctxForCancl, cancel := context.WithCancel(context.Background()) + + tests := []struct { + name string + args args + want want + }{ + { + name: "success check", + args: args{ + ctx: context.Background(), + uri: m.endpoint, + cancel: nil, + }, + want: want{ + errFunc: assert.NoError, + }, + }, + { + name: "cancel context", + args: args{ + ctx: ctxForCancl, + cancel: cancel, + uri: "mongodb://" + m.endpoint, + }, + want: want{ + errFunc: assert.Error, + }, + }, + } + + for _, tt := range tests { + m.Run(tt.name, func() { + tt := tt + instance, err := NewMongo(tt.args.ctx, tt.args.uri) + assert.NoError(t, err) + checker := instance.HealthChecker(5 * time.Second) + + if tt.args.cancel != nil { + tt.args.cancel() + } + + err = checker.Check(tt.args.ctx) + tt.want.errFunc(t, err) + }) + } +} + +func TestMongoTestSuite(t *testing.T) { + t.Parallel() + suite.Run(t, new(MongoTestSuite)) +} diff --git a/engine/checker.go b/engine/checker.go index 4802929..e31f90c 100644 --- a/engine/checker.go +++ b/engine/checker.go @@ -1,27 +1,29 @@ package engine +import "context" + type Checker interface { - Check() error - Name() string + Check(ctx context.Context) error + GetName() string } // DefaultChecker is a default implementation of Checker // if you want simple checker without implementation Checker interface // tou can call this function and receive Checker interface type DefaultChecker struct { - CheckFunc func() error `json:"check"` - NameCheck string `json:"name"` + CheckFunc func(ctx context.Context) error `json:"check"` + NameCheck string `json:"name"` } -func (c *DefaultChecker) Check() error { - return c.CheckFunc() +func (c *DefaultChecker) Check(ctx context.Context) error { + return c.CheckFunc(ctx) } -func (c *DefaultChecker) Name() string { +func (c *DefaultChecker) GetName() string { return c.NameCheck } -func NewDefaultChecker(name string, checkFunc func() error) *DefaultChecker { +func NewDefaultChecker(name string, checkFunc func(ctx context.Context) error) *DefaultChecker { return &DefaultChecker{ CheckFunc: checkFunc, NameCheck: name, diff --git a/engine/closer.go b/engine/closer.go index 495b0b0..e85fc34 100644 --- a/engine/closer.go +++ b/engine/closer.go @@ -2,32 +2,31 @@ package engine import ( "context" - "sync" ) type Closer interface { GetName() string - Close(ctx context.Context, wg *sync.WaitGroup) error + Close(ctx context.Context) error } // DefaultCloser is a default implementation of Closer // if you want simple checker without implementation Closer interface // tou can call this function and receive Closer interface type DefaultCloser struct { - Name string `json:"name"` - CloseFunc func(ctx context.Context, wg *sync.WaitGroup) error `json:"close"` + Name string `json:"name"` + CloseFunc func(ctx context.Context) error `json:"close"` } func (d *DefaultCloser) GetName() string { return d.Name } -func (d *DefaultCloser) Close(ctx context.Context, wg *sync.WaitGroup) error { - return d.CloseFunc(ctx, wg) +func (d *DefaultCloser) Close(ctx context.Context) error { + return d.CloseFunc(ctx) } -func NewDefaultCloser(name string, close func(ctx context.Context, group *sync.WaitGroup) error) Closer { +func NewDefaultCloser(name string, close func(ctx context.Context) error) Closer { return &DefaultCloser{ Name: name, CloseFunc: close, diff --git a/engine/debug.go b/engine/debug.go index cf7d983..0109450 100644 --- a/engine/debug.go +++ b/engine/debug.go @@ -2,42 +2,54 @@ package engine import ( "context" + "encoding/json" "net/http" "sync" "time" "github.com/glebnaz/witcher/metrics" - "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" ) type DebugServer struct { PORT string `json:"port" envconfig:"PORT" default:":8084"` - engine *echo.Echo - m sync.Mutex + mux *http.ServeMux + server *http.Server - ready bool - checkers []Checker + m sync.RWMutex + + ready bool + checkersGroup []Checker } -func NewDebugServer(port string) *DebugServer { - e := echo.New() - e.Debug = false - e.HideBanner = true - e.HidePort = true +var readHeaderTimeout = 2 * time.Minute +// NewDebugServer create new debug server +// +// port is debug port +// use value with `:` for example: :8084 +func NewDebugServer(port string) *DebugServer { debug := &DebugServer{ - PORT: port, - ready: false, - checkers: make([]Checker, 0, 10), + PORT: port, + ready: false, + checkersGroup: make([]Checker, 0, 10), } - e.GET("/ready", debug.Ready) - e.GET("/live", debug.Live) - e.GET("/metrics", echo.WrapHandler(metrics.Handler())) - wrapPProf(e) - debug.engine = e + mux := http.NewServeMux() + mux.HandleFunc("/read", debug.ReadinessProbeHandler) + mux.HandleFunc("/live", debug.LiveProbeHandler) + mux.HandleFunc("/startup", debug.StartupProbeHandler) + mux.Handle("/metrics", metrics.Handler()) + wrapPProf(mux) + + debug.mux = mux + + debug.server = &http.Server{ + Addr: debug.PORT, + Handler: mux, + ReadHeaderTimeout: readHeaderTimeout, + } return debug } @@ -57,10 +69,10 @@ func (d *DebugServer) SetReady(ready bool) { } func (d *DebugServer) AddChecker(checker Checker) { - log.Debug().Msgf("Adding checker %s", checker.Name()) + log.Debug().Msgf("Adding checker %s", checker.GetName()) d.m.Lock() defer d.m.Unlock() - d.checkers = append(d.checkers, checker) + d.checkersGroup = append(d.checkersGroup, checker) } // AddCheckers for check your server is live @@ -70,50 +82,82 @@ func (d *DebugServer) AddCheckers(checkers []Checker) { } } -// Live is probe checker -func (d *DebugServer) Live(c echo.Context) error { - log.Info().Msgf("Live check at %s", time.Now()) - d.m.Lock() - defer d.m.Unlock() +// ReadinessProbeHandler is probe checker for k8s readiness probe +// +// It will check all checkersGroup and return 200 if all checkersGroup is ok +// or 500 if one of them is not ok +// +// response will be json with all checkersGroup and their status +func (d *DebugServer) ReadinessProbeHandler(w http.ResponseWriter, req *http.Request) { + log.Info().Msgf("readiness probe at %s", time.Now()) + d.m.RLock() + defer d.m.RUnlock() info := make(map[string]bool) live := true - for i := range d.checkers { - if err := d.checkers[i].Check(); err != nil { - log.Error().Msgf("Server is not live: %s", err) + for i := range d.checkersGroup { + if err := d.checkersGroup[i].Check(req.Context()); err != nil { + log.Error().Msgf("Server is not ready to receive traffic: %s", err) live = false - info[d.checkers[i].Name()] = false + info[d.checkersGroup[i].GetName()] = false } else { - info[d.checkers[i].Name()] = true + info[d.checkersGroup[i].GetName()] = true } } + jsonInfo, err := json.Marshal(info) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !live { - log.Error().Msgf("Server is not live") - return c.JSON(http.StatusInternalServerError, info) + log.Error().Msgf("Server is not ready to receive traffic") + w.WriteHeader(http.StatusInternalServerError) + //nolint:errcheck + w.Write(jsonInfo) + return } - return c.JSON(http.StatusOK, info) + w.WriteHeader(http.StatusOK) + //nolint:errcheck + w.Write(jsonInfo) +} + +// LiveProbeHandler is probe checker for k8s liveness probe +func (d *DebugServer) LiveProbeHandler(w http.ResponseWriter, _ *http.Request) { + log.Info().Msgf("live probe at %s", time.Now()) + w.WriteHeader(http.StatusOK) + //nolint:errcheck + w.Write([]byte("OK")) } -// Ready is probe checker -func (d *DebugServer) Ready(c echo.Context) error { - log.Info().Msgf("Ready check at %s", time.Now()) +// StartupProbeHandler is probe checker for k8s startup probe +// ready or not ready +// used only for start +func (d *DebugServer) StartupProbeHandler(w http.ResponseWriter, _ *http.Request) { + log.Info().Msgf("StartupProbe check at %s", time.Now()) if d.ready { - return c.String(http.StatusOK, "OK") + w.WriteHeader(http.StatusOK) + // nolint:errcheck + w.Write([]byte("OK")) + } else { + w.WriteHeader(http.StatusInternalServerError) + // nolint:errcheck + w.Write([]byte("Not ready")) } - return c.String(http.StatusInternalServerError, "Not ready") } func (d *DebugServer) RunDebug() error { + d.server.Addr = d.PORT log.Info().Msgf("Run debug server at %s", time.Now()) - return d.engine.Start(d.PORT) + return d.server.ListenAndServe() } func (d *DebugServer) ShutdownDebug(ctx context.Context) error { log.Info().Msgf("Start shutdown debug server at %s", time.Now()) - errShutDown := d.engine.Shutdown(ctx) + errShutDown := d.server.Shutdown(ctx) if errShutDown != nil { return errShutDown } diff --git a/engine/engine.go b/engine/engine.go index 3d282c2..0a2ad0a 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -10,11 +10,10 @@ import ( "sync" "time" - "google.golang.org/grpc" - "github.com/kelseyhightower/envconfig" "github.com/oklog/run" "github.com/rs/zerolog/log" + "google.golang.org/grpc" ) type engineCfg struct { @@ -33,7 +32,7 @@ func WithDisableBanner() ServerOpt { // WithShutdownTimeout set shutdown timeout // -// timeout is duration for graceful shutdown +// is duration for graceful shutdown func WithShutdownTimeout(timeout time.Duration) ServerOpt { return func(s *Server) { s.shutdownTimeout = timeout @@ -148,13 +147,14 @@ func (s *Server) runRunGroup() { } }() - s.AddChecker(NewDefaultChecker("run group", func() error { + s.AddChecker(NewDefaultChecker("run group", func(_ context.Context) error { return s.getErrRunGroup() })) } // AddActor add actor control you background task -// you have execute function and done function(interrupt function) +// +// you have executed function and done function(interrupt function) // interrupt function handle the error // execute function is called when server is ready func (s *Server) AddActor(execute func() error, interrupt func(err error)) { @@ -221,23 +221,37 @@ func (s *Server) Shutdown() error { } } +type ErrorCloser struct { + data map[string]error +} + +func (e *ErrorCloser) Error() string { + var err []string + for k, v := range e.data { + err = append(err, fmt.Sprintf("Error Close %s: %s", k, v)) + } + return strings.Join(err, "\n") +} + func (s *Server) closeClosers(ctx context.Context) error { var wg sync.WaitGroup - var errMsg []string + errCloser := ErrorCloser{data: make(map[string]error)} + for _, closer := range s.closerGroup { log.Debug().Msgf("Close closer: %s", closer.GetName()) wg.Add(1) - go func(c Closer) { - err := c.Close(ctx, &wg) + go func(c Closer, wg *sync.WaitGroup) { + defer wg.Done() + err := c.Close(ctx) if err != nil { - errMsg = append(errMsg, fmt.Sprintf("Error Close Closer %s: %s", c.GetName(), err)) + errCloser.data[c.GetName()] = err log.Error().Msgf("Error Close Closer %s: %s", c.GetName(), err) } - }(closer) + }(closer, &wg) } wg.Wait() - if len(errMsg) > 0 { - return errors.New(strings.Join(errMsg, "\n")) + if len(errCloser.data) > 0 { + return &errCloser } log.Debug().Msg("Close all closers gracefully") return nil diff --git a/engine/engine_test.go b/engine/engine_test.go new file mode 100644 index 0000000..bb76999 --- /dev/null +++ b/engine/engine_test.go @@ -0,0 +1,408 @@ +package engine + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// TestDebugServer tests the DebugServer +// +// will check the all k8s probes +func TestDebugServer(t *testing.T) { + t.Parallel() + t.Log("Testing DEBUG server") + + type fields struct { + e *Server + waitTime *time.Duration + ctx context.Context + } + + type ProbeResult struct { + StatusCode int + Body string + CallerError assert.ErrorAssertionFunc + } + + type ReadyProbeResult struct { + StatusCode int + Body map[string]bool + CallError assert.ErrorAssertionFunc + } + + type want struct { + Ready ReadyProbeResult + Live ProbeResult + StartUp ProbeResult + RunErrorFunc assert.ErrorAssertionFunc + } + + tests := []struct { + name string + fields func(p string) fields + Port string + want want + }{ + { + name: "Simple Positive Test(With out checker)", + fields: func(p string) fields { + e := NewServer(WithDebugPort(p)) + + ctx := context.Background() + + return fields{ + e: e, + ctx: ctx, + } + }, + Port: getUniquePort(), + want: want{ + Ready: ReadyProbeResult{ + StatusCode: http.StatusOK, + Body: map[string]bool{ + "run group": true, + }, + CallError: assert.NoError, + }, + Live: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + StartUp: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + RunErrorFunc: assert.NoError, + }, + }, + { + name: "Simple Positive Test(With checker)", + Port: getUniquePort(), + fields: func(p string) fields { + e := NewServer(WithDebugPort(p)) + + checker := NewDefaultChecker("checker true", func(ctx context.Context) error { + return nil + }) + + e.AddCheckers([]Checker{checker}) + + time := 1 * time.Second + + ctx := context.Background() + + return fields{ + e: e, + waitTime: &time, + ctx: ctx, + } + }, + want: want{ + Ready: ReadyProbeResult{ + StatusCode: http.StatusOK, + Body: map[string]bool{ + "run group": true, + "checker true": true, + }, + CallError: assert.NoError, + }, + Live: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + StartUp: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + RunErrorFunc: assert.NoError, + }, + }, + { + name: "Simple Negative Test(With checker)", + Port: getUniquePort(), + fields: func(p string) fields { + e := NewServer(WithDebugPort(p)) + + checker := NewDefaultChecker("checker false", func(ctx context.Context) error { + return assert.AnError + }) + + e.AddCheckers([]Checker{checker}) + + time := 1 * time.Second + + ctx := context.Background() + + return fields{ + e: e, + waitTime: &time, + ctx: ctx, + } + }, + want: want{ + Ready: ReadyProbeResult{ + StatusCode: http.StatusInternalServerError, + Body: map[string]bool{ + "run group": true, + "checker false": false, + }, + CallError: assert.NoError, + }, + Live: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + StartUp: ProbeResult{ + StatusCode: http.StatusOK, + Body: "OK", + CallerError: assert.NoError, + }, + RunErrorFunc: assert.NoError, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + f := tt.fields(tt.Port) + e := f.e + go func() { + err := e.Run() + tt.want.RunErrorFunc(t, err) + }() + + t.Cleanup(func() { + errShutdown := e.Shutdown() + assert.NoError(t, errShutdown) + }) + + // wait for the server to start + if f.waitTime != nil { + time.Sleep(*f.waitTime) + } + + // check the ready probe + resp, err := makeRequest(f.ctx, host+tt.Port+"/read") + tt.want.Ready.CallError(t, err) + if tt.want.Ready.Body != nil { + bodyByte, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + readyData := map[string]bool{} + err = json.Unmarshal(bodyByte, &readyData) + assert.NoError(t, err) + assert.Equal(t, tt.want.Ready.Body, readyData) + assert.Equal(t, tt.want.Ready.StatusCode, resp.StatusCode) + } + + // check the live probe + resp, err = makeRequest(f.ctx, host+tt.Port+"/live") + tt.want.Live.CallerError(t, err) + if tt.want.Live.Body != "" { + bodyByte, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.Equal(t, tt.want.Live.Body, string(bodyByte)) + assert.Equal(t, tt.want.Live.StatusCode, resp.StatusCode) + } + + // check the startup probe + resp, err = makeRequest(f.ctx, host+tt.Port+"/startup") + tt.want.StartUp.CallerError(t, err) + if tt.want.StartUp.Body != "" { + bodyByte, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.Equal(t, tt.want.Live.Body, string(bodyByte)) + assert.Equal(t, tt.want.Live.StatusCode, resp.StatusCode) + } + + }) + } +} + +func makeRequest(ctx context.Context, url string) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + return resp, nil +} + +const ( + liveHandlerPostfix = "/live" + host = "http://localhost" +) + +type portContainer struct { + last int + m sync.Mutex +} + +var pContainer = portContainer{ + last: 3500, // use this values because gh actions restricts the port range +} + +func getUniquePort() string { + pContainer.m.Lock() + last := pContainer.last + newPort := last + 1 + pContainer.last = newPort + pContainer.m.Unlock() + return fmt.Sprintf(":%d", newPort) +} + +func TestWithDebugPort(t *testing.T) { + t.Parallel() + //get random int from 1000 to 9999 + portString := getUniquePort() + s := NewServer(WithDebugPort(portString)) + assert.Equal(t, portString, s.DebugServer.PORT) + + go func() { + err := s.Run() + assert.NoError(t, err) + }() + + time.Sleep(5 * time.Millisecond) + + resp, err := makeRequest(context.Background(), host+portString+liveHandlerPostfix) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestDebugPortFromEnv(t *testing.T) { + t.Parallel() + portString := getUniquePort() + + //set env + err := os.Setenv("DEBUG_PORT", portString) + assert.NoError(t, err) + s := NewServer() + go func() { + err := s.Run() + assert.NoError(t, err) + }() + + time.Sleep(5 * time.Millisecond) + + resp, err := makeRequest(context.Background(), host+portString+liveHandlerPostfix) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestServer_AddCloser(t *testing.T) { + t.Parallel() + s := NewServer() + closer := NewDefaultCloser("closer", func(ctx context.Context) error { + return nil + }) + s.AddCloser(closer) + assert.Equal(t, 1, len(s.closerGroup)) + assert.Equal(t, "closer", s.closerGroup[0].GetName()) +} + +func TestServer_AddChecker(t *testing.T) { + t.Parallel() + s := NewServer() + checker := NewDefaultChecker("checker", func(ctx context.Context) error { + return nil + }) + s.AddChecker(checker) + assert.Equal(t, 1, len(s.checkersGroup)) + assert.Equal(t, "checker", s.checkersGroup[0].GetName()) +} + +func TestServer_RunGroup(t *testing.T) { + t.Parallel() + port := getUniquePort() + s := NewServer(WithDebugPort(port)) + + data := make(chan int) + + s.AddActor(func() error { + for i := 0; i < 10; i++ { + data <- i + } + err := s.Shutdown() + assert.NoError(t, err) + return nil + }, func(error) { + assert.NoError(t, nil) + }) + + go func() { + err := s.Run() + assert.NoError(t, err) + }() + + value := make([]int, 0, 10) + for { + i := <-data + value = append(value, i) + + if len(value) == 10 { + break + } + } + + assert.Equal(t, 10, len(value)) +} + +func TestPProfHandlers(t *testing.T) { + t.Parallel() + mux := http.NewServeMux() + wrapPProf(mux) + + tests := []struct { + name string + path string + }{ + {"Index", "/debug/pprof/"}, + {"Heap", "/debug/pprof/heap"}, + {"Goroutine", "/debug/pprof/goroutine"}, + {"Block", "/debug/pprof/block"}, + {"ThreadCreate", "/debug/pprof/threadcreate"}, + {"Cmdline", "/debug/pprof/cmdline"}, + {"Profile", "/debug/pprof/profile"}, + {"Symbol", "/debug/pprof/symbol"}, + {"Trace", "/debug/pprof/trace"}, + {"Mutex", "/debug/pprof/mutex"}, + {"Allocs", "/debug/pprof/allocs"}, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + //nolint:noctx + req, err := http.NewRequest("GET", tt.path, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + mux.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code) + }) + } +} diff --git a/engine/pprof.go b/engine/pprof.go index ac7468e..01e7bd4 100644 --- a/engine/pprof.go +++ b/engine/pprof.go @@ -1,125 +1,29 @@ package engine import ( + "net/http" "net/http/pprof" - - "github.com/labstack/echo/v4" ) -func wrapPProf(g *echo.Echo) { +func wrapPProf(mux *http.ServeMux) { routers := []struct { - Method string Path string - Handler echo.HandlerFunc + Handler http.HandlerFunc }{ - {"GET", "/", IndexHandler()}, - {"GET", "/heap", HeapHandler()}, - {"GET", "/goroutine", GoroutineHandler()}, - {"GET", "/block", BlockHandler()}, - {"GET", "/threadcreate", ThreadCreateHandler()}, - {"GET", "/cmdline", CmdlineHandler()}, - {"GET", "/profile", ProfileHandler()}, - {"GET", "/symbol", SymbolHandler()}, - {"POST", "/symbol", SymbolHandler()}, - {"GET", "/trace", TraceHandler()}, - {"GET", "/mutex", MutexHandler()}, - {"GET", "/allocs", AllocsHandler()}, + {"/debug/pprof/", pprof.Index}, + {"/debug/pprof/heap", pprof.Handler("heap").ServeHTTP}, + {"/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP}, + {"/debug/pprof/block", pprof.Handler("block").ServeHTTP}, + {"/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP}, + {"/debug/pprof/cmdline", pprof.Cmdline}, + {"/debug/pprof/profile", pprof.Profile}, + {"/debug/pprof/symbol", pprof.Symbol}, + {"/debug/pprof/trace", pprof.Trace}, + {"/debug/pprof/mutex", pprof.Handler("mutex").ServeHTTP}, + {"/debug/pprof/allocs", pprof.Handler("allocs").ServeHTTP}, } for _, r := range routers { - switch r.Method { - case "GET": - g.GET(r.Path, r.Handler) - case "POST": - g.POST(r.Path, r.Handler) - } - } -} - -// IndexHandler will pass the call from /debug/pprof to pprof. -func IndexHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Index(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// HeapHandler will pass the call from /debug/pprof/heap to pprof. -func HeapHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("heap").ServeHTTP(ctx.Response(), ctx.Request()) - return nil - } -} - -// GoroutineHandler will pass the call from /debug/pprof/goroutine to pprof. -func GoroutineHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("goroutine").ServeHTTP(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// BlockHandler will pass the call from /debug/pprof/block to pprof. -func BlockHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("block").ServeHTTP(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// ThreadCreateHandler will pass the call from /debug/pprof/threadcreate to pprof. -func ThreadCreateHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("threadcreate").ServeHTTP(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// CmdlineHandler will pass the call from /debug/pprof/cmdline to pprof. -func CmdlineHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Cmdline(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// ProfileHandler will pass the call from /debug/pprof/profile to pprof. -func ProfileHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Profile(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// SymbolHandler will pass the call from /debug/pprof/symbol to pprof. -func SymbolHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Symbol(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// TraceHandler will pass the call from /debug/pprof/trace to pprof. -func TraceHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Trace(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// MutexHandler will pass the call from /debug/pprof/mutex to pprof. -func MutexHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("mutex").ServeHTTP(ctx.Response().Writer, ctx.Request()) - return nil - } -} - -// AllocsHandler will pass the call from /debug/pprof/allocs to pprof. -func AllocsHandler() echo.HandlerFunc { - return func(ctx echo.Context) error { - pprof.Handler("allocs").ServeHTTP(ctx.Response().Writer, ctx.Request()) - return nil + mux.HandleFunc(r.Path, r.Handler) } } diff --git a/example/service/main.go b/example/service/main.go index 0ddf43b..769a364 100644 --- a/example/service/main.go +++ b/example/service/main.go @@ -3,7 +3,6 @@ package main import ( "context" "os" - "sync" "time" "github.com/glebnaz/witcher/db/mongo" @@ -18,21 +17,20 @@ func main() { serverGRPC := grpc.NewDefaultServer("witcher_example") s := engine.NewServer(engine.WithGRPCServer(serverGRPC, ":8082", time.Second*5)) - checker1 := engine.NewDefaultChecker("checker false", func() error { + checker1 := engine.NewDefaultChecker("checker false", func(ctx context.Context) error { log.Info().Msgf("Checker1 is running") return nil }) - checker2 := engine.NewDefaultChecker("checker true", func() error { + checker2 := engine.NewDefaultChecker("checker true", func(ctx context.Context) error { log.Info().Msgf("Checker2 is running") return nil }) - closer := engine.NewDefaultCloser("closer", func(ctx context.Context, wg *sync.WaitGroup) error { + closer := engine.NewDefaultCloser("closer", func(ctx context.Context) error { log.Info().Msgf("Closer is running") time.Sleep(time.Second * 10) log.Info().Msgf("Closer is running after 10 seconds") - wg.Done() return nil }) diff --git a/go.mod b/go.mod index 101f9f0..ebc0c63 100644 --- a/go.mod +++ b/go.mod @@ -3,42 +3,83 @@ module github.com/glebnaz/witcher go 1.21 require ( - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/labstack/echo/v4 v4.10.2 github.com/oklog/run v1.1.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.15.1 github.com/rs/zerolog v1.31.0 + github.com/stretchr/testify v1.9.0 + github.com/testcontainers/testcontainers-go v0.29.1 go.mongodb.org/mongo-driver v1.11.6 - google.golang.org/grpc v1.55.0 + google.golang.org/grpc v1.58.3 ) require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/docker v25.0.3+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/klauspost/compress v1.16.5 // indirect - github.com/labstack/gommon v0.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/montanaflynn/stats v0.7.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f // indirect - google.golang.org/protobuf v1.30.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index db061e7..67bd70a 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,51 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= +github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -15,12 +54,18 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -30,28 +75,42 @@ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3x 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.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= -github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= -github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= -github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= @@ -65,18 +124,32 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.29.1 h1:z8kxdFlovA2y97RWx98v/TQ+tR+SXZm6p35M+xB92zk= +github.com/testcontainers/testcontainers-go v0.29.1/go.mod h1:SnKnKQav8UcgtKqjp/AD8bE1MqZm+3TDb/B8crE3XnI= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -88,43 +161,81 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o= go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -132,24 +243,38 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f h1:QNVuVEP2S7NNxLdNdOq0RiW3c9pW4gIpUUd+GAOjk1Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/log/test_log.go b/log/test_log.go new file mode 100644 index 0000000..9934032 --- /dev/null +++ b/log/test_log.go @@ -0,0 +1,72 @@ +package log + +import ( + "io" + "os" + + "github.com/rs/zerolog" +) + +type TestLoggerOps func(t *TestLogger) + +// WithTestLoggerLevel use for set custom level for TestLogger +// as level use zerolog.Level +func WithTestLoggerLevel(lvl zerolog.Level) TestLoggerOps { + return func(t *TestLogger) { + t.lvl = lvl + } +} + +// WithTestLoggerWriter use for set custom writer for TestLogger +func WithTestLoggerWriter(w io.Writer) TestLoggerOps { + return func(t *TestLogger) { + t.w = w + } +} + +// WithTestLoggerName use for set custom name for TestLogger +// as name use string +func WithTestLoggerName(name string) TestLoggerOps { + return func(t *TestLogger) { + t.testName = name + } +} + +type TestLogger struct { + z zerolog.Logger + + // --- custom fields --- + testName string + w io.Writer + lvl zerolog.Level +} + +const testNameKey = "test_name" + +func (t *TestLogger) Printf(format string, v ...any) { + t.z.Info().Msgf(format, v...) +} + +// NewTestLogger returns new instance of TestLogger +// you need provide stdout for your log +// this function will init default logger with default formatter +// +// as default TestLogger use Info level +// +// as default TestLogger use os.Stdout +// +// as default TestLogger use "unknown" as test name +func NewTestLogger(opts ...TestLoggerOps) *TestLogger { + tl := &TestLogger{ + testName: "unknown", + lvl: zerolog.InfoLevel, + w: os.Stdout, + } + + for _, opt := range opts { + opt(tl) + } + + tl.z = zerolog.New(tl.w).With().Str(testNameKey, tl.testName).Logger() + return tl +} diff --git a/metrics/http_mw.go b/metrics/http_mw.go index 31c7665..8d1d18f 100644 --- a/metrics/http_mw.go +++ b/metrics/http_mw.go @@ -2,23 +2,20 @@ package metrics import ( "fmt" + "net/http" "time" - - "github.com/labstack/echo/v4" ) -// EchoMiddleware is a middleware for echo framework -// It collects metrics for http requests -// It returns a function that takes a handler and returns a handler/ -// This is a pattern for echo middleware -// https://echo.labstack.com/guide/middleware -// https://echo.labstack.com/cookbook/middleware -// https://echo.labstack.com/cookbook/middleware#custom-middleware +// HTTPMiddleware is a middleware for the standard net/http package. +// +// It collects metrics for HTTP requests. +// It returns a function that takes a handler and returns a handler. +// This is a pattern for net/http middleware. // -// // For every RPC it exports the following metrics: -// // - server_http_request_count{method,path,code} -// // - server_http_response_time{method,path} -func EchoMiddleware(namespace string) echo.MiddlewareFunc { +// For every request, it exports the following metrics: +// - server_http_request_count{method,path,code} +// - server_http_response_time{method,path} +func HTTPMiddleware(namespace string) func(http.Handler) http.Handler { var serverRequestCounter = MustRegisterCounterVec("server_http_request_count", namespace, "server_request_count", []string{"method", "path", "code"}) @@ -27,15 +24,15 @@ func EchoMiddleware(namespace string) echo.MiddlewareFunc { namespace, "server response time in seconds", TimeBucketsMedium, []string{"method", "path"}) - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { startTime := time.Now() - err := next(c) - statusCode := fmt.Sprintf("%d", c.Response().Status) + next.ServeHTTP(w, r) + statusCode := fmt.Sprintf("%d", http.StatusOK) // replace with actual status code if available tookTime := float64(time.Since(startTime)) / float64(time.Second) - serverResponseTime.WithLabelValues(c.Request().Method, c.Request().RequestURI).Observe(tookTime) - serverRequestCounter.WithLabelValues(c.Request().Method, c.Request().RequestURI, statusCode).Inc() - return err - } + serverResponseTime.WithLabelValues(r.Method, r.RequestURI).Observe(tookTime) + serverRequestCounter.WithLabelValues(r.Method, r.RequestURI, statusCode).Inc() + }) } }