From 86d15cd3e0d7c1d8b3a0d6b82d2bd0d92eb74509 Mon Sep 17 00:00:00 2001 From: bubbajoe Date: Sat, 25 May 2024 14:27:35 +0900 Subject: [PATCH] replace slog with zap --- TODO.md | 4 + cmd/dgate-server/main.go | 11 +- go.mod | 2 + go.sum | 4 + internal/admin/admin_api.go | 75 ++++----- internal/admin/admin_fsm.go | 45 +++--- internal/admin/admin_raft.go | 69 +++++--- internal/admin/admin_routes.go | 35 ++-- internal/admin/changestate/change_state.go | 8 +- .../changestate/testutil/change_state.go | 5 +- internal/admin/routes/collection_routes.go | 3 +- internal/admin/routes/domain_routes.go | 3 +- internal/admin/routes/misc_routes.go | 5 +- internal/admin/routes/module_routes.go | 3 +- internal/admin/routes/module_routes_test.go | 13 +- internal/admin/routes/namespace_routes.go | 3 +- .../admin/routes/namespace_routes_test.go | 7 +- internal/admin/routes/route_routes.go | 3 +- internal/admin/routes/route_routes_test.go | 7 +- internal/admin/routes/secret_routes.go | 3 +- internal/admin/routes/service_routes.go | 5 +- internal/admin/routes/service_routes_test.go | 8 +- internal/config/config.go | 74 ++++++--- internal/config/configtest/dgate_configs.go | 7 +- internal/proxy/change_log.go | 56 ++++--- internal/proxy/change_log_test.go | 14 +- internal/proxy/dynamic_proxy.go | 150 ++++++------------ internal/proxy/proxy_handler.go | 31 ++-- internal/proxy/proxy_handler_test.go | 7 +- internal/proxy/proxy_printer.go | 6 +- internal/proxy/proxy_state.go | 109 +++++-------- internal/proxy/proxy_state_test.go | 33 ++-- internal/proxy/proxystore/proxy_store.go | 18 ++- pkg/cache/cache.go | 4 +- pkg/modules/extractors/runtime_test.go | 5 +- pkg/raftadmin/raftadmin.go | 49 +++--- pkg/raftadmin/raftadmin_client.go | 7 +- pkg/raftadmin/raftadmin_test.go | 13 +- pkg/rafthttp/rafthttp.go | 9 +- pkg/rafthttp/rafthttp_test.go | 11 +- pkg/scheduler/scheduler.go | 9 +- pkg/spec/change_log.go | 7 +- pkg/storage/badger_logger.go | 9 +- pkg/storage/debug_storage.go | 5 +- pkg/storage/file_storage.go | 5 +- pkg/storage/memory_storage.go | 4 +- pkg/util/logadapter/zap2badger.go | 36 +++++ pkg/util/logadapter/zap2hc.go | 135 ++++++++++++++++ pkg/util/logger/logging.go | 135 ---------------- 49 files changed, 654 insertions(+), 615 deletions(-) create mode 100644 pkg/util/logadapter/zap2badger.go create mode 100644 pkg/util/logadapter/zap2hc.go delete mode 100644 pkg/util/logger/logging.go diff --git a/TODO.md b/TODO.md index d1965db..0122759 100644 --- a/TODO.md +++ b/TODO.md @@ -154,3 +154,7 @@ Make it easier to debug modules by adding more logging and error handling. This Add stack tracing for typescript modules. + +## Decouple Admin API from Raft Implementation + +Currently, Raft Implementation is tightly coupled with the Admin API. This makes it difficult to change the Raft Implementation without changing the Admin API. Decouple the Raft Implementation from the Admin API to make it easier to change the Raft Implementation. \ No newline at end of file diff --git a/cmd/dgate-server/main.go b/cmd/dgate-server/main.go index 34cbaef..0f1a8ed 100644 --- a/cmd/dgate-server/main.go +++ b/cmd/dgate-server/main.go @@ -60,8 +60,14 @@ func main() { fmt.Printf("Error loading config: %s\n", err) os.Exit(1) } else { - proxyState := proxy.StartProxyGateway(version, dgateConfig) - admin.StartAdminAPI(dgateConfig, proxyState) + logger, err := dgateConfig.GetLogger() + if err != nil { + fmt.Printf("Error setting up logger: %s\n", err) + os.Exit(1) + } + defer logger.Sync() + proxyState := proxy.NewProxyState(logger.Named("proxy"), dgateConfig) + admin.StartAdminAPI(version, dgateConfig, logger.Named("admin"), proxyState) if err := proxyState.Start(); err != nil { fmt.Printf("Error loading config: %s\n", err) os.Exit(1) @@ -75,7 +81,6 @@ func main() { ) <-sigchan proxyState.Stop() - os.Exit(1) } } } diff --git a/go.mod b/go.mod index 3b2c600..3b9198e 100644 --- a/go.mod +++ b/go.mod @@ -80,6 +80,8 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/sdk v1.26.0 // indirect go.opentelemetry.io/otel/trace v1.26.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/go.sum b/go.sum index 8ba5aae..3c6b16a 100644 --- a/go.sum +++ b/go.sum @@ -278,6 +278,10 @@ go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZH go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/internal/admin/admin_api.go b/internal/admin/admin_api.go index ba5fb52..5b4297e 100644 --- a/internal/admin/admin_api.go +++ b/internal/admin/admin_api.go @@ -13,39 +13,56 @@ import ( "github.com/dgate-io/dgate/internal/admin/changestate" "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/util" - - "log/slog" + "go.uber.org/zap" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) -func StartAdminAPI(conf *config.DGateConfig, cs changestate.ChangeState) { +func StartAdminAPI( + version string, conf *config.DGateConfig, + logger *zap.Logger, cs changestate.ChangeState, +) { if conf.AdminConfig == nil { - cs.Logger().Warn("Admin API is disabled") + logger.Warn("Admin API is disabled") return } - // Start HTTP Server mux := chi.NewRouter() - configureRoutes(mux, cs, conf) + configureRoutes(mux, version, + logger.Named("routes"), cs, conf) + + // Start HTTP Server + go func() { + adminHttpLogger := logger.Named("http") + hostPort := fmt.Sprintf("%s:%d", + conf.AdminConfig.Host, conf.AdminConfig.Port) + logger.Info("Starting admin api on " + hostPort) + server := &http.Server{ + Addr: hostPort, + Handler: mux, + ErrorLog: zap.NewStdLog(adminHttpLogger), + } + if err := server.ListenAndServe(); err != nil { + panic(err) + } + }() // Start HTTPS Server go func() { if conf.AdminConfig.TLS != nil { - adminHttpsLogHandler := cs.Logger(). - Handler().WithGroup("admin-https") + adminHttpsLog := logger.Named("https") secureHostPort := fmt.Sprintf("%s:%d", conf.AdminConfig.Host, conf.AdminConfig.TLS.Port) secureServer := &http.Server{ Addr: secureHostPort, Handler: mux, - ErrorLog: slog.NewLogLogger(adminHttpsLogHandler, slog.LevelInfo), + ErrorLog: zap.NewStdLog(adminHttpsLog), } - cs.Logger().Info("Starting secure admin api on" + secureHostPort) - cs.Logger().Debug("TLS Cert", - "cert_file", conf.AdminConfig.TLS.CertFile, - "key_file", conf.AdminConfig.TLS.KeyFile, + logger.Info("Starting secure admin api on" + secureHostPort) + logger.Debug("TLS Cert", + zap.String("cert_file", conf.AdminConfig.TLS.CertFile), + zap.String("key_file", conf.AdminConfig.TLS.KeyFile), ) if err := secureServer.ListenAndServeTLS( conf.AdminConfig.TLS.CertFile, @@ -59,13 +76,13 @@ func StartAdminAPI(conf *config.DGateConfig, cs changestate.ChangeState) { // Start Test Server if conf.TestServerConfig != nil { if !conf.Debug { - cs.Logger().Warn("Test server is disabled in non-debug mode") + logger.Warn("Test server is disabled in non-debug mode") } else { go func() { testHostPort := fmt.Sprintf("%s:%d", conf.TestServerConfig.Host, conf.TestServerConfig.Port) - mux := chi.NewRouter() - mux.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { + testMux := chi.NewRouter() + testMux.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/debug") { // strip /debug prefix r.URL.Path = strings.TrimPrefix(r.URL.Path, "/debug") @@ -102,13 +119,11 @@ func StartAdminAPI(conf *config.DGateConfig, cs changestate.ChangeState) { util.JsonResponse(w, http.StatusOK, respMap) }) - testServerLogger := cs.Logger(). - WithGroup("test-server") - + testServerLogger := logger.Named("test-server") testServer := &http.Server{ Addr: testHostPort, - Handler: mux, - ErrorLog: slog.NewLogLogger(testServerLogger.Handler(), slog.LevelInfo), + Handler: testMux, + ErrorLog: zap.NewStdLog(testServerLogger), } if conf.TestServerConfig.EnableHTTP2 { h2Server := &http2.Server{} @@ -117,10 +132,10 @@ func StartAdminAPI(conf *config.DGateConfig, cs changestate.ChangeState) { panic(err) } if conf.TestServerConfig.EnableH2C { - testServer.Handler = h2c.NewHandler(mux, h2Server) + testServer.Handler = h2c.NewHandler(testServer.Handler, h2Server) } } - cs.Logger().Info("Starting test server on " + testHostPort) + logger.Info("Starting test server on " + testHostPort) if err := testServer.ListenAndServe(); err != nil { panic(err) @@ -128,18 +143,4 @@ func StartAdminAPI(conf *config.DGateConfig, cs changestate.ChangeState) { }() } } - go func() { - adminHttpLogger := cs.Logger().WithGroup("admin-http") - hostPort := fmt.Sprintf("%s:%d", - conf.AdminConfig.Host, conf.AdminConfig.Port) - cs.Logger().Info("Starting admin api on " + hostPort) - server := &http.Server{ - Addr: hostPort, - Handler: mux, - ErrorLog: slog.NewLogLogger(adminHttpLogger.Handler(), slog.LevelInfo), - } - if err := server.ListenAndServe(); err != nil { - panic(err) - } - }() } diff --git a/internal/admin/admin_fsm.go b/internal/admin/admin_fsm.go index 2820030..5632be1 100644 --- a/internal/admin/admin_fsm.go +++ b/internal/admin/admin_fsm.go @@ -3,26 +3,22 @@ package admin import ( "encoding/json" "io" - "log/slog" "github.com/dgate-io/dgate/internal/admin/changestate" "github.com/dgate-io/dgate/pkg/spec" "github.com/hashicorp/raft" + "go.uber.org/zap" ) type dgateAdminFSM struct { cs changestate.ChangeState - logger *slog.Logger + logger *zap.Logger } var _ raft.BatchingFSM = (*dgateAdminFSM)(nil) -func newDGateAdminFSM(cs changestate.ChangeState) *dgateAdminFSM { - dgateFSMLogger := cs.Logger().WithGroup("admin-raft-fsm") - return &dgateAdminFSM{ - cs: cs, - logger: dgateFSMLogger, - } +func newDGateAdminFSM(logger *zap.Logger, cs changestate.ChangeState) *dgateAdminFSM { + return &dgateAdminFSM{cs, logger} } func (fsm *dgateAdminFSM) isReplay(log *raft.Log) bool { @@ -35,15 +31,13 @@ func (fsm *dgateAdminFSM) checkLast(log *raft.Log) { rft := fsm.cs.Raft() if !fsm.cs.Ready() && fsm.isReplay(log) { fsm.logger.Info("FSM is not ready, setting ready", - "Index", log.Index, - "AIndex", rft.AppliedIndex(), - "LIndex", rft.LastIndex(), + zap.Uint64("index", log.Index), + zap.Uint64("applied-index", rft.AppliedIndex()), + zap.Uint64("last-index", rft.LastIndex()), ) defer func() { if err := fsm.cs.ReloadState(false); err != nil { - fsm.logger.Error("Error processing change log in FSM", - "error", err, - ) + fsm.logger.Error("Error processing change log in FSM", zap.Error(err)) } else { fsm.cs.SetReady() } @@ -56,12 +50,12 @@ func (fsm *dgateAdminFSM) applyLog(log *raft.Log) (*spec.ChangeLog, error) { case raft.LogCommand: var cl spec.ChangeLog if err := json.Unmarshal(log.Data, &cl); err != nil { - fsm.logger.Error("Error unmarshalling change log") + fsm.logger.Error("Error unmarshalling change log", zap.Error(err)) return nil, err } else if cl.Cmd.IsNoop() { return nil, nil } else if cl.ID == "" { - fsm.logger.Error("Change log ID is empty") + fsm.logger.Error("Change log ID is empty", zap.Error(err)) panic("change log ID is empty") } // find a way to apply only if latest index to save time @@ -72,16 +66,17 @@ func (fsm *dgateAdminFSM) applyLog(log *raft.Log) (*spec.ChangeLog, error) { servers := raft.DecodeConfiguration(log.Data).Servers for i, server := range servers { fsm.logger.Debug("configuration update server", - "address", server.Address, "index", i, + zap.Any("address", server.Address), + zap.Int("index", i), ) } case raft.LogBarrier: err := fsm.cs.WaitForChanges() if err != nil { - fsm.logger.Error("Error waiting for changes", "error", err) + fsm.logger.Error("Error waiting for changes", zap.Error(err)) } default: - fsm.cs.Logger().Error("Unknown log type in FSM Apply") + fsm.logger.Error("Unknown log type in FSM Apply") } return nil, nil } @@ -97,11 +92,11 @@ func (fsm *dgateAdminFSM) ApplyBatch(logs []*raft.Log) []any { if fsm.isReplay(lastLog) { rft := fsm.cs.Raft() fsm.logger.Info("applying log batch logs", - "size", len(logs), - "current", lastLog.Index, - "applied", rft.AppliedIndex(), - "commit", rft.CommitIndex(), - "last", rft.LastIndex(), + zap.Int("size", len(logs)), + zap.Uint64("current", lastLog.Index), + zap.Uint64("applied", rft.AppliedIndex()), + zap.Uint64("commit", rft.CommitIndex()), + zap.Uint64("last", rft.LastIndex()), ) } cls := make([]*spec.ChangeLog, 0, len(logs)) @@ -112,7 +107,7 @@ func (fsm *dgateAdminFSM) ApplyBatch(logs []*raft.Log) []any { } if err := fsm.cs.ReloadState(true, cls...); err != nil { - fsm.logger.Error("Error reloading state @ FSM ApplyBatch") + fsm.logger.Error("Error reloading state @ FSM ApplyBatch", zap.Error(err)) } }() diff --git a/internal/admin/admin_raft.go b/internal/admin/admin_raft.go index b966252..e31f4dd 100644 --- a/internal/admin/admin_raft.go +++ b/internal/admin/admin_raft.go @@ -14,12 +14,19 @@ import ( "github.com/dgate-io/dgate/pkg/raftadmin" "github.com/dgate-io/dgate/pkg/rafthttp" "github.com/dgate-io/dgate/pkg/storage" - "github.com/dgate-io/dgate/pkg/util/logger" + "github.com/dgate-io/dgate/pkg/util/logadapter" raftbadgerdb "github.com/dgate-io/raft-badger" + "github.com/dgraph-io/badger/v4" "github.com/hashicorp/raft" + "go.uber.org/zap" ) -func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeState) { +func setupRaft( + server *chi.Mux, + logger *zap.Logger, + conf *config.DGateConfig, + cs changestate.ChangeState, +) { adminConfig := conf.AdminConfig var sstore raft.StableStore var lstore raft.LogStore @@ -33,8 +40,11 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS if err != nil { panic(fmt.Errorf("invalid config: %s", err)) } - badgerStore, err := raftbadgerdb.NewBadgerStore( - path.Join(fileConfig.Directory, "raft"), + badgerLogger := logadapter.NewZap2BadgerAdapter(logger.Named("badger-file")) + raftDir := path.Join(fileConfig.Directory, "raft") + badgerStore, err := raftbadgerdb.New( + badger.DefaultOptions(raftDir). + WithLogger(badgerLogger), ) if err != nil { panic(err) @@ -59,18 +69,17 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS // TODO: Support snapshots SnapshotInterval: time.Hour * 24, SnapshotThreshold: ^uint64(0), - Logger: logger.NewSLogHCAdapter( - cs.Logger().WithGroup("admin-raft"), - ), + Logger: logadapter.NewZap2HCLogAdapter(logger), }, ) + advertAddr := adminConfig.Replication.AdvertAddr if advertAddr == "" { advertAddr = fmt.Sprintf("%s:%d", adminConfig.Host, adminConfig.Port) } address := raft.ServerAddress(advertAddr) - raftHttpLogger := cs.Logger().WithGroup("raft-http") + raftHttpLogger := logger.Named("http") if adminConfig.Replication.AdvertScheme != "http" && adminConfig.Replication.AdvertScheme != "https" { panic(fmt.Errorf("invalid scheme: %s", adminConfig.Replication.AdvertScheme)) } @@ -80,7 +89,7 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS adminConfig.Replication.AdvertScheme+"://(address)/raft", ) raftNode, err := raft.NewRaft( - raftConfig, newDGateAdminFSM(cs), + raftConfig, newDGateAdminFSM(logger.Named("fsm"), cs), lstore, sstore, snapstore, transport, ) if err != nil { @@ -91,7 +100,7 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS // Setup raft handler server.Handle("/raft/*", transport) - raftAdminLogger := cs.Logger().WithGroup("raft-admin") + raftAdminLogger := logger.Named("admin") raftAdmin := raftadmin.NewRaftAdminHTTPServer( raftNode, raftAdminLogger, []raft.ServerAddress{address}, ) @@ -115,8 +124,8 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS } serverConfig := configFuture.Configuration() - cs.Logger().With("config", adminConfig). - Debug("Replication config") + logger.Debug("Replication config", + zap.Any("config", serverConfig)) if adminConfig.Replication.BootstrapCluster { raftNode.BootstrapCluster(raft.Configuration{ @@ -132,8 +141,8 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS go func() { addresses := make([]string, 0) if adminConfig.Replication.DiscoveryDomain != "" { - cs.Logger().Debug("no previous configuration found, attempting to discover cluster", - "domain", adminConfig.Replication.DiscoveryDomain, + logger.Debug("no previous configuration found, attempting to discover cluster", + zap.String("domain", adminConfig.Replication.DiscoveryDomain), ) addrs, err := net.LookupHost(adminConfig.Replication.DiscoveryDomain) if err != nil { @@ -142,7 +151,8 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS if len(addrs) == 0 { panic(fmt.Errorf("no addrs found for %s", adminConfig.Replication.DiscoveryDomain)) } - cs.Logger().Info("discovered addresses", "addresses", addrs) + logger.Info("discovered addresses", + zap.Strings("addresses", addrs)) for _, addr := range addrs { if addr[len(addr)-1] == '.' { addr = addr[:len(addr)-1] @@ -167,7 +177,7 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS } adminClient := raftadmin.NewHTTPAdminClient(doer, adminConfig.Replication.AdvertScheme+"://(address)/raftadmin", - cs.Logger().WithGroup("raft-admin-client"), + logger.Named("raft-admin-client"), ) RETRY: for _, url := range addresses { @@ -177,17 +187,24 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS continue } if retries > 15 { - cs.Logger().Error("Skipping verifying leader at %s: %s", url, err) + logger.Error("Skipping verifying leader", + zap.String("url", url), zap.Error(err), + ) continue } retries += 1 - cs.Logger().Debug("Retrying verifying leader at %s: %s", url, err) + logger.Debug("Retrying verifying leader", + zap.String("url", url), zap.Error(err)) <-time.After(3 * time.Second) goto RETRY } // If this node is watch only, add it as a non-voter node, otherwise add it as a voter node if adminConfig.WatchOnly { - cs.Logger().Info("Adding non-voter", "url", url) + logger.Info("Adding non-voter", + zap.String("id", raftId), + zap.String("leader", adminConfig.Replication.AdvertAddr), + zap.String("url", url), + ) resp, err := adminClient.AddNonvoter( context.Background(), raft.ServerAddress(url), &raftadmin.AddNonvoterRequest{ @@ -202,8 +219,11 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS panic(resp.Error) } } else { - cs.Logger().Info("Adding voter: %s - leader: %s", - adminConfig.Replication.AdvertAddr, url) + logger.Info("Adding voter: %s - leader: %s", + zap.String("id", raftId), + zap.String("leader", adminConfig.Replication.AdvertAddr), + zap.String("url", url), + ) resp, err := adminClient.AddVoter(context.Background(), raft.ServerAddress(url), &raftadmin.AddVoterRequest{ ID: raftId, Address: adminConfig.Replication.AdvertAddr, @@ -221,11 +241,12 @@ func setupRaft(conf *config.DGateConfig, server *chi.Mux, cs changestate.ChangeS panic(err) } } else { - cs.Logger().Warn("no admin urls specified, waiting to be added to cluster") + logger.Warn("no admin urls specified, waiting to be added to cluster") } }() } else { - cs.Logger().Debug("previous configuration found", - "servers", serverConfig.Servers) + logger.Debug("previous configuration found", + zap.Any("servers", serverConfig.Servers), + ) } } diff --git a/internal/admin/admin_routes.go b/internal/admin/admin_routes.go index 4cf41e2..a3d0ae3 100644 --- a/internal/admin/admin_routes.go +++ b/internal/admin/admin_routes.go @@ -20,9 +20,16 @@ import ( api "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/sdk/metric" + "go.uber.org/zap" ) -func configureRoutes(server *chi.Mux, cs changestate.ChangeState, conf *config.DGateConfig) { +func configureRoutes( + server *chi.Mux, + version string, + logger *zap.Logger, + cs changestate.ChangeState, + conf *config.DGateConfig, +) { adminConfig := conf.AdminConfig server.Use(func(next http.Handler) http.Handler { ipList := iplist.NewIPList() @@ -81,8 +88,9 @@ func configureRoutes(server *chi.Mux, cs changestate.ChangeState, conf *config.D for i := 0; i < count; i++ { allowed, err = ipList.Contains(xForwardedForIps[i]) if err != nil { - cs.Logger().Error("error checking x-forwarded-for ip", - "x-forwarded-for_ip", xForwardedForIps[i], "error", err, + logger.Error("error checking x-forwarded-for ip", + zap.String("x-forwarded-for_ip", xForwardedForIps[i]), + zap.Error(err), ) if conf.Debug { http.Error(w, "Bad Request: could not parse x-forwarded-for IP address", http.StatusBadRequest) @@ -169,23 +177,26 @@ func configureRoutes(server *chi.Mux, cs changestate.ChangeState, conf *config.D })) if adminConfig.Replication != nil { - setupRaft(conf, server, cs) + setupRaft(server, logger.Named("raft"), conf, cs) } if adminConfig != nil { server.Route("/api/v1", func(api chi.Router) { - routes.ConfigureRouteAPI(api, cs, conf) - routes.ConfigureModuleAPI(api, cs, conf) - routes.ConfigureServiceAPI(api, cs, conf) - routes.ConfigureNamespaceAPI(api, cs, conf) - routes.ConfigureDomainAPI(api, cs, conf) - routes.ConfigureCollectionAPI(api, cs, conf) - routes.ConfigureSecretAPI(api, cs, conf) + apiLogger := logger.Named("api") + routes.ConfigureRouteAPI( + api, + apiLogger, cs, conf) + routes.ConfigureModuleAPI(api, apiLogger, cs, conf) + routes.ConfigureServiceAPI(api, apiLogger, cs, conf) + routes.ConfigureNamespaceAPI(api, apiLogger, cs, conf) + routes.ConfigureDomainAPI(api, apiLogger, cs, conf) + routes.ConfigureCollectionAPI(api, apiLogger, cs, conf) + routes.ConfigureSecretAPI(api, apiLogger, cs, conf) }) } server.Group(func(misc chi.Router) { routes.ConfigureChangeLogAPI(misc, cs, conf) - routes.ConfigureHealthAPI(misc, cs, conf) + routes.ConfigureHealthAPI(misc, version, cs) if setupMetricProvider(conf) { misc.Handle("/metrics", promhttp.Handler()) } diff --git a/internal/admin/changestate/change_state.go b/internal/admin/changestate/change_state.go index eb3dee8..f5bcc5f 100644 --- a/internal/admin/changestate/change_state.go +++ b/internal/admin/changestate/change_state.go @@ -1,8 +1,6 @@ package changestate import ( - "log/slog" - "github.com/dgate-io/dgate/internal/proxy" "github.com/dgate-io/dgate/pkg/resources" "github.com/dgate-io/dgate/pkg/spec" @@ -15,6 +13,7 @@ type ChangeState interface { ProcessChangeLog(*spec.ChangeLog, bool) error WaitForChanges() error ReloadState(bool, ...*spec.ChangeLog) error + ChangeHash() uint32 // Readiness Ready() bool @@ -27,11 +26,6 @@ type ChangeState interface { // Resources ResourceManager() *resources.ResourceManager DocumentManager() resources.DocumentManager - - // Misc - Logger() *slog.Logger - ChangeHash() uint32 - Version() string } var _ ChangeState = (*proxy.ProxyState)(nil) diff --git a/internal/admin/changestate/testutil/change_state.go b/internal/admin/changestate/testutil/change_state.go index 4a30cf1..ea1626f 100644 --- a/internal/admin/changestate/testutil/change_state.go +++ b/internal/admin/changestate/testutil/change_state.go @@ -9,6 +9,7 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/hashicorp/raft" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) type MockChangeState struct { @@ -36,8 +37,8 @@ func (m *MockChangeState) ResourceManager() *resources.ResourceManager { } // Logger implements changestate.ChangeState. -func (m *MockChangeState) Logger() *slog.Logger { - return m.Called().Get(0).(*slog.Logger) +func (m *MockChangeState) Logger() *zap.Logger { + return m.Called().Get(0).(*zap.Logger) } // ProcessChangeLog implements changestate.ChangeState. diff --git a/internal/admin/routes/collection_routes.go b/internal/admin/routes/collection_routes.go index 6ba28d4..b423c2c 100644 --- a/internal/admin/routes/collection_routes.go +++ b/internal/admin/routes/collection_routes.go @@ -12,9 +12,10 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" "github.com/santhosh-tekuri/jsonschema/v5" + "go.uber.org/zap" ) -func ConfigureCollectionAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureCollectionAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() dm := cs.DocumentManager() server.Put("/collection", func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/admin/routes/domain_routes.go b/internal/admin/routes/domain_routes.go index e1d4423..06879c3 100644 --- a/internal/admin/routes/domain_routes.go +++ b/internal/admin/routes/domain_routes.go @@ -11,9 +11,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureDomainAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureDomainAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/domain", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) diff --git a/internal/admin/routes/misc_routes.go b/internal/admin/routes/misc_routes.go index d3f1457..a471bbe 100644 --- a/internal/admin/routes/misc_routes.go +++ b/internal/admin/routes/misc_routes.go @@ -42,10 +42,9 @@ func ConfigureChangeLogAPI(server chi.Router, cs changestate.ChangeState, appCon }) } -func ConfigureHealthAPI(server chi.Router, cs changestate.ChangeState, _ *config.DGateConfig) { +func ConfigureHealthAPI(server chi.Router, version string, cs changestate.ChangeState) { healthlyResp := []byte( - `{"status":"ok","version":"` + - cs.Version() + `"}`, + `{"status":"ok","version":"` + version + `"}`, ) server.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/internal/admin/routes/module_routes.go b/internal/admin/routes/module_routes.go index 4c81e83..75bb1fa 100644 --- a/internal/admin/routes/module_routes.go +++ b/internal/admin/routes/module_routes.go @@ -11,9 +11,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureModuleAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureModuleAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/module", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) diff --git a/internal/admin/routes/module_routes_test.go b/internal/admin/routes/module_routes_test.go index 7e6b6dd..ed1383d 100644 --- a/internal/admin/routes/module_routes_test.go +++ b/internal/admin/routes/module_routes_test.go @@ -16,16 +16,17 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) func TestAdminRoutes_Module(t *testing.T) { namespaces := []string{"default", "test"} for _, ns := range namespaces { config := configtest.NewTest3DGateConfig() - ps := proxy.NewProxyState(config) + ps := proxy.NewProxyState(zap.NewNop(), config) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureModuleAPI(r, ps, config) + routes.ConfigureModuleAPI(r, zap.NewNop(), ps, config) }) server := httptest.NewServer(mux) defer server.Close() @@ -40,11 +41,11 @@ func TestAdminRoutes_Module(t *testing.T) { if err := client.CreateModule(&spec.Module{ Name: "test", NamespaceName: ns, - Payload: base64.StdEncoding.EncodeToString( + Payload: base64.StdEncoding.EncodeToString( []byte("\"use test\""), ), - Type: spec.ModuleTypeJavascript, - Tags: []string{"test123"}, + Type: spec.ModuleTypeJavascript, + Tags: []string{"test123"}, }); err != nil { t.Fatal(err) } @@ -86,7 +87,7 @@ func TestAdminRoutes_ModuleError(t *testing.T) { cs.On("ResourceManager").Return(rm) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureModuleAPI(r, cs, config) + routes.ConfigureModuleAPI(r, zap.NewNop(), cs, config) }) server := httptest.NewServer(mux) defer server.Close() diff --git a/internal/admin/routes/namespace_routes.go b/internal/admin/routes/namespace_routes.go index f597b39..0cc0563 100644 --- a/internal/admin/routes/namespace_routes.go +++ b/internal/admin/routes/namespace_routes.go @@ -11,9 +11,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureNamespaceAPI(server chi.Router, cs changestate.ChangeState, _ *config.DGateConfig) { +func ConfigureNamespaceAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, _ *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/namespace", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) diff --git a/internal/admin/routes/namespace_routes_test.go b/internal/admin/routes/namespace_routes_test.go index 0efdf43..00cc227 100644 --- a/internal/admin/routes/namespace_routes_test.go +++ b/internal/admin/routes/namespace_routes_test.go @@ -15,16 +15,17 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) func TestAdminRoutes_Namespace(t *testing.T) { namespaces := []string{"_test", "default"} for _, ns := range namespaces { config := configtest.NewTest3DGateConfig() - ps := proxy.NewProxyState(config) + ps := proxy.NewProxyState(zap.NewNop(), config) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureNamespaceAPI(r, ps, config) + routes.ConfigureNamespaceAPI(r, zap.NewNop(), ps, config) }) server := httptest.NewServer(mux) defer server.Close() @@ -80,7 +81,7 @@ func TestAdminRoutes_NamespaceError(t *testing.T) { cs.On("ResourceManager").Return(rm) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureNamespaceAPI(r, cs, config) + routes.ConfigureNamespaceAPI(r, zap.NewNop(), cs, config) }) server := httptest.NewServer(mux) defer server.Close() diff --git a/internal/admin/routes/route_routes.go b/internal/admin/routes/route_routes.go index 074a84c..d60813f 100644 --- a/internal/admin/routes/route_routes.go +++ b/internal/admin/routes/route_routes.go @@ -11,9 +11,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureRouteAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureRouteAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/route", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) diff --git a/internal/admin/routes/route_routes_test.go b/internal/admin/routes/route_routes_test.go index ac9e401..e8efc07 100644 --- a/internal/admin/routes/route_routes_test.go +++ b/internal/admin/routes/route_routes_test.go @@ -15,16 +15,17 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) func TestAdminRoutes_Route(t *testing.T) { namespaces := []string{"default", "test"} for _, ns := range namespaces { config := configtest.NewTest3DGateConfig() - ps := proxy.NewProxyState(config) + ps := proxy.NewProxyState(zap.NewNop(), config) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureRouteAPI(r, ps, config) + routes.ConfigureRouteAPI(r, zap.NewNop(), ps, config) }) server := httptest.NewServer(mux) defer server.Close() @@ -83,7 +84,7 @@ func TestAdminRoutes_RouteError(t *testing.T) { cs.On("ResourceManager").Return(rm) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureRouteAPI(r, cs, config) + routes.ConfigureRouteAPI(r, zap.NewNop(), cs, config) }) server := httptest.NewServer(mux) defer server.Close() diff --git a/internal/admin/routes/secret_routes.go b/internal/admin/routes/secret_routes.go index fec1e1d..56156f4 100644 --- a/internal/admin/routes/secret_routes.go +++ b/internal/admin/routes/secret_routes.go @@ -12,9 +12,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureSecretAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureSecretAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/secret", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) diff --git a/internal/admin/routes/service_routes.go b/internal/admin/routes/service_routes.go index 94f554b..082ec8f 100644 --- a/internal/admin/routes/service_routes.go +++ b/internal/admin/routes/service_routes.go @@ -12,9 +12,10 @@ import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) -func ConfigureServiceAPI(server chi.Router, cs changestate.ChangeState, appConfig *config.DGateConfig) { +func ConfigureServiceAPI(server chi.Router, logger *zap.Logger, cs changestate.ChangeState, appConfig *config.DGateConfig) { rm := cs.ResourceManager() server.Put("/service", func(w http.ResponseWriter, r *http.Request) { eb, err := io.ReadAll(r.Body) @@ -65,7 +66,7 @@ func ConfigureServiceAPI(server chi.Router, cs changestate.ChangeState, appConfi } if repl := cs.Raft(); repl != nil { - cs.Logger().Debug("Waiting for raft barrier") + logger.Debug("Waiting for raft barrier") future := repl.Barrier(time.Second * 5) if err := future.Error(); err != nil { util.JsonError(w, http.StatusInternalServerError, err.Error()) diff --git a/internal/admin/routes/service_routes_test.go b/internal/admin/routes/service_routes_test.go index 444adbc..2e808a1 100644 --- a/internal/admin/routes/service_routes_test.go +++ b/internal/admin/routes/service_routes_test.go @@ -15,16 +15,17 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) func TestAdminRoutes_Service(t *testing.T) { namespaces := []string{"default", "test"} for _, ns := range namespaces { config := configtest.NewTest3DGateConfig() - ps := proxy.NewProxyState(config) + ps := proxy.NewProxyState(zap.NewNop(), config) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureServiceAPI(r, ps, config) + routes.ConfigureServiceAPI(r, zap.NewNop(), ps, config) }) server := httptest.NewServer(mux) defer server.Close() @@ -82,7 +83,8 @@ func TestAdminRoutes_ServiceError(t *testing.T) { cs.On("ResourceManager").Return(rm) mux := chi.NewMux() mux.Route("/api/v1", func(r chi.Router) { - routes.ConfigureServiceAPI(r, cs, config) + routes.ConfigureServiceAPI( + r, zap.NewNop(), cs, config) }) server := httptest.NewServer(mux) defer server.Close() diff --git a/internal/config/config.go b/internal/config/config.go index 837fea9..c7665d7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,17 +1,20 @@ package config import ( - "log/slog" "time" "github.com/dgate-io/dgate/pkg/spec" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) type ( - LoggerLevel string DGateConfig struct { Version string `koanf:"version"` - LogLevel *LoggerLevel `koanf:"log_level"` + LogLevel string `koanf:"log_level,string"` + LogJson bool `koanf:"log_json"` + LogColor bool `koanf:"log_color"` + Logging *LoggingConfig `koanf:"Logger"` NodeId string `koanf:"node_id"` Storage DGateStorageConfig `koanf:"storage"` ProxyConfig DGateProxyConfig `koanf:"proxy"` @@ -24,6 +27,16 @@ type ( DisableDefaultNamespace bool `koanf:"disable_default_namespace"` } + LoggingConfig struct { + ZapConfig *zap.Config `koanf:",squash"` + LogOutputs []*LogOutput `koanf:"log_outputs"` + } + + LogOutput struct { + Name string `koanf:"name"` + Config map[string]any `koanf:",remain"` + } + DGateProxyConfig struct { Host string `koanf:"host"` Port int `koanf:"port"` @@ -140,7 +153,6 @@ type ( } DGateHttpTransportConfig struct { - // DNSServer string `koanf:"dns_server"` DNSTimeout time.Duration `koanf:"dns_timeout"` DNSPreferGo bool `koanf:"dns_prefer_go"` @@ -202,25 +214,41 @@ const ( AuthMethodJWTAuth AuthMethodType = "jwt" ) -func (l *LoggerLevel) String() string { - return string(*l) -} -func (l *LoggerLevel) Level() slog.Level { - switch *l { - case "debug", "trace": - return slog.LevelDebug - case "info": - return slog.LevelInfo - case "warn": - return slog.LevelWarn - case "error": - return slog.LevelError - default: - return slog.LevelInfo +func (conf *DGateConfig) GetLogger() (*zap.Logger, error) { + level, err := zap.ParseAtomicLevel(conf.LogLevel) + if err != nil { + return nil, err } -} + if conf.Logging == nil { + conf.Logging = &LoggingConfig{} + } + if conf.Logging.ZapConfig == nil { + config := zap.NewProductionConfig() + config.Level = level + config.Development = conf.Debug + config.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder + config.OutputPaths = []string{"stdout"} + + if config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder; conf.LogColor { + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + } + + if config.Encoding = "console"; conf.LogJson { + config.InitialFields = map[string]interface{}{ + "version": conf.Version, + "server_tags": conf.Tags, + "node_id": conf.NodeId, + } + config.Encoding = "json" + } -func NewLoggerLevel(level string) *LoggerLevel { - l := LoggerLevel(level) - return &l + conf.Logging.ZapConfig = &config + } + + if logger, err := conf.Logging.ZapConfig.Build(); err != nil { + return nil, err + } else { + logger.Sync() + return logger, nil + } } diff --git a/internal/config/configtest/dgate_configs.go b/internal/config/configtest/dgate_configs.go index 8a3ccda..ae5a673 100644 --- a/internal/config/configtest/dgate_configs.go +++ b/internal/config/configtest/dgate_configs.go @@ -3,11 +3,16 @@ package configtest import ( "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/pkg/spec" + "go.uber.org/zap" ) func NewTestDGateConfig() *config.DGateConfig { return &config.DGateConfig{ - LogLevel: config.NewLoggerLevel("info"), + Logging: &config.LoggingConfig{ + ZapConfig: &zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + }, + }, DisableDefaultNamespace: true, Debug: true, Version: "v1", diff --git a/internal/proxy/change_log.go b/internal/proxy/change_log.go index 44d6fd3..207fd8d 100644 --- a/internal/proxy/change_log.go +++ b/internal/proxy/change_log.go @@ -6,12 +6,11 @@ import ( "errors" - "log/slog" - "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/util/sliceutil" "github.com/dgraph-io/badger/v4" "github.com/mitchellh/mapstructure" + "go.uber.org/zap" ) // processChangeLog - processes a change log and applies the change to the proxy state @@ -26,72 +25,72 @@ func (ps *ProxyState) processChangeLog(cl *spec.ChangeLog, reload, store bool) ( var item spec.Namespace item, err = decode[spec.Namespace](cl.Item) if err == nil { - ps.logger.Debug("Processing namespace", "name", item.Name) + ps.logger.Debug("Processing namespace", zap.String("name", item.Name)) err = ps.processNamespace(&item, cl) } case spec.Services: var item spec.Service item, err = decode[spec.Service](cl.Item) if err == nil { - ps.logger.Debug("Processing service", "name", item.Name) + ps.logger.Debug("Processing service", zap.String("name", item.Name)) err = ps.processService(&item, cl) } case spec.Routes: var item spec.Route item, err = decode[spec.Route](cl.Item) if err == nil { - ps.logger.Debug("Processing route", "name", item.Name) + ps.logger.Debug("Processing route", zap.String("name", item.Name)) err = ps.processRoute(&item, cl) } case spec.Modules: var item spec.Module item, err = decode[spec.Module](cl.Item) if err == nil { - ps.logger.Debug("Processing module", "name", item.Name) + ps.logger.Debug("Processing module", zap.String("name", item.Name)) err = ps.processModule(&item, cl) } case spec.Domains: var item spec.Domain item, err = decode[spec.Domain](cl.Item) if err == nil { - ps.logger.Debug("Processing domain", "name", item.Name) + ps.logger.Debug("Processing domain", zap.String("name", item.Name)) err = ps.processDomain(&item, cl) } case spec.Collections: var item spec.Collection item, err = decode[spec.Collection](cl.Item) if err == nil { - ps.logger.Debug("Processing collection", "name", item.Name) + ps.logger.Debug("Processing collection", zap.String("name", item.Name)) err = ps.processCollection(&item, cl) } case spec.Documents: var item spec.Document item, err = decode[spec.Document](cl.Item) if err == nil { - ps.logger.Debug("Processing document", "id", item.ID) + ps.logger.Debug("Processing document", zap.String("id", item.ID)) err = ps.processDocument(&item, cl) } case spec.Secrets: var item spec.Secret item, err = decode[spec.Secret](cl.Item) if err == nil { - ps.logger.Debug("Processing secret", "name", item.Name) + ps.logger.Debug("Processing secret", zap.String("name", item.Name)) err = ps.processSecret(&item, cl) } default: err = fmt.Errorf("unknown command: %s", cl.Cmd) } if err != nil { - ps.logger.With("error", err).Error("decoding or processing change log") + ps.logger.Error("decoding or processing change log", zap.Error(err)) return } } if reload { if cl.Cmd.Resource().IsRelatedTo(spec.Routes) || cl.Cmd.IsNoop() { - ps.logger.Debug("Registering change log", "cmd", cl.Cmd) - err = ps.reconfigureState(false) + ps.logger.Debug("Registering change log", zap.Stringer("cmd", cl.Cmd)) + err = ps.reconfigureState(false, cl) if err != nil { - ps.logger.With("error", err).Error("Error registering change log") + ps.logger.Error("Error registering change log", zap.Error(err)) return } // update change log hash only when the change is successfully applied @@ -101,7 +100,7 @@ func (ps *ProxyState) processChangeLog(cl *spec.ChangeLog, reload, store bool) ( if !ps.config.Debug { return err } - ps.logger.With("error", err).Error("error updating change log hash") + ps.logger.Error("error updating change log hash", zap.Error(err)) } else { ps.changeHash = changeHash } @@ -111,7 +110,7 @@ func (ps *ProxyState) processChangeLog(cl *spec.ChangeLog, reload, store bool) ( if err = ps.store.StoreChangeLog(cl); err != nil { // TODO: find a way to revert the change and reload the state // TODO: OR add flag in config to ignore storage errors - ps.logger.With("error", err).Error("Error storing change log") + ps.logger.Error("Error storing change log", zap.Error(err)) return } } @@ -264,10 +263,8 @@ func (ps *ProxyState) applyChange(changeLog *spec.ChangeLog) <-chan error { } } changeLog.SetErrorChan(done) - select { - case ps.changeChan <- changeLog: - case <-time.After(time.Second * 5): - done <- errors.New("timeout pushing change log change") + if err := ps.processChangeLog(changeLog, true, true); err != nil { + done <- err } return done } @@ -282,17 +279,17 @@ func (ps *ProxyState) restoreFromChangeLogs(directApply bool) error { return errors.New("failed to get state change logs from storage: " + err.Error()) } } else { - ps.logger.Info("restoring state change logs from storage", "count", len(logs)) + ps.logger.Info("restoring state change logs from storage", zap.Int("count", len(logs))) // we might need to sort the change logs by timestamp for i, cl := range logs { ps.logger.Debug("restoring change log", - "index", i, "changeLog", cl.Cmd, + zap.Int("index", i), + zap.Stringer("changeLog", cl.Cmd), ) err = ps.processChangeLog(cl, false, false) if err != nil { if ps.config.Debug { - ps.logger.With("error", err). - Error("error restorng from change logs") + ps.logger.Error("error restorng from change logs", zap.Error(err)) continue } return err @@ -303,7 +300,7 @@ func (ps *ProxyState) restoreFromChangeLogs(directApply bool) error { return err } } else { - if err = ps.reconfigureState(false); err != nil { + if err = ps.reconfigureState(false, nil); err != nil { return nil } } @@ -312,12 +309,13 @@ func (ps *ProxyState) restoreFromChangeLogs(directApply bool) error { if len(logs) > 1 { removed, err := ps.compactChangeLogs(logs) if err != nil { - ps.logger.With("error", err).Error("failed to compact state change logs") + ps.logger.Error("failed to compact state change logs", zap.Error(err)) return err } if removed > 0 { ps.logger.Info("compacted change logs", - "removed", removed, "total", len(logs), + zap.Int("removed", removed), + zap.Int("total", len(logs)), ) } } @@ -341,7 +339,7 @@ compaction rules: - if an add command is followed by a delete command with matching keys, remove both commands - if an add command is followed by another add command with matching keys, remove the first add command */ -func compactChangeLogsRemoveList(logger *slog.Logger, logs []*spec.ChangeLog) []*spec.ChangeLog { +func compactChangeLogsRemoveList(logger *zap.Logger, logs []*spec.ChangeLog) []*spec.ChangeLog { removeList := make([]*spec.ChangeLog, 0) iterations := 0 START: @@ -381,7 +379,7 @@ START: } prevLog = curLog } - logger.Debug("compacted change logs", "iterations", iterations) + logger.Debug("compacted change logs", zap.Int("iterations", iterations)) // remove duplicates from list removeList = sliceutil.SliceUnique(removeList, func(cl *spec.ChangeLog) string { return cl.ID }) diff --git a/internal/proxy/change_log_test.go b/internal/proxy/change_log_test.go index 9798cb2..675eccd 100644 --- a/internal/proxy/change_log_test.go +++ b/internal/proxy/change_log_test.go @@ -1,11 +1,11 @@ package proxy import ( - "log/slog" "strconv" "testing" "github.com/dgate-io/dgate/pkg/spec" + "go.uber.org/zap" ) func TestCompactChangeLog_DifferentNamespace(t *testing.T) { @@ -34,7 +34,7 @@ func TestCompactChangeLog_DifferentNamespace(t *testing.T) { }, } setSequentialChangeLogs(logs) - removeList := compactChangeLogsRemoveList(slog.Default(), logs) + removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) testChangeLogRemoveList(tt, removeList) }) } @@ -66,7 +66,7 @@ func TestCompactChangeLog_SameNamespace(t *testing.T) { }, } setSequentialChangeLogs(logs) - removeList := compactChangeLogsRemoveList(slog.Default(), logs) + removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) testChangeLogRemoveList(tt, removeList, 0, 1) }) } @@ -86,7 +86,7 @@ func TestCompactChangeLog_Mirror(t *testing.T) { newCommonChangeLog(spec.DeleteNamespaceCommand), } setSequentialChangeLogs(logs) - removeList := compactChangeLogsRemoveList(slog.Default(), logs) + removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) testChangeLogRemoveList(t, removeList, 4, 5, 3, 6, 2, 7, 1, 8, 0, 9) } @@ -102,7 +102,7 @@ func TestCompactChangeLog_Noop(t *testing.T) { newCommonChangeLog(spec.AddModuleCommand), } setSequentialChangeLogs(logs) - removeList := compactChangeLogsRemoveList(slog.Default(), logs) + removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) testChangeLogRemoveList(t, removeList, 0, 1, 2, 5) } @@ -112,7 +112,7 @@ func TestCompactChangeLog_AddDelete(t *testing.T) { newCommonChangeLog(spec.DeleteNamespaceCommand), } setSequentialChangeLogs(logs) - removeList := compactChangeLogsRemoveList(slog.Default(), logs) + removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) testChangeLogRemoveList(t, removeList, 0, 1) } @@ -126,7 +126,7 @@ func TestCompactChangeLog_AddDeleteDiffNamespaces(t *testing.T) { // newCommonChangeLog(spec.DeleteNamespaceCommand, "t2", "test-ns2"), // } // setSequentialChangeLogs(logs) - // removeList := compactChangeLogsRemoveList(slog.Default(), logs) + // removeList := compactChangeLogsRemoveList(zap.NewNop(), logs) // testChangeLogRemoveList(t, removeList, 0, 1, 2, 3) } diff --git a/internal/proxy/dynamic_proxy.go b/internal/proxy/dynamic_proxy.go index e291827..79e4c55 100644 --- a/internal/proxy/dynamic_proxy.go +++ b/internal/proxy/dynamic_proxy.go @@ -7,37 +7,43 @@ import ( "os" "time" - "log/slog" - - "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/internal/router" "github.com/dgate-io/dgate/pkg/modules/extractors" "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/typescript" "github.com/dgate-io/dgate/pkg/util/tree/avl" "github.com/dop251/goja" + "go.uber.org/zap" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) -func (state *ProxyState) reconfigureState(init bool) (err error) { +func (ps *ProxyState) reconfigureState(init bool, log *spec.ChangeLog) (err error) { + defer func() { + if err != nil { + ps.restartState(func(err error) { + ps.logger.Error("Error restarting state", zap.Error(err)) + ps.Stop() + }) + } + log.PushError(err) + }() + start := time.Now() - if err = state.setupModules(); err != nil { + if err = ps.setupModules(); err != nil { return } - if err = state.setupRoutes(); err != nil { + if err = ps.setupRoutes(); err != nil { return } elapsed := time.Since(start) if !init { - state.logger.Debug( - "State reloaded", - "elapsed", elapsed, + ps.logger.Debug("State reloaded", + zap.Duration("elapsed", elapsed), ) } else { - state.logger.Info( - "State initialized", - "elapsed", elapsed, + ps.logger.Info("State initialized", + zap.Duration("elapsed", elapsed), ) } return nil @@ -56,13 +62,13 @@ func (ps *ProxyState) setupModules() error { start := time.Now() if mod.Type == spec.ModuleTypeTypescript { if modPayload, err = typescript.Transpile(modPayload); err != nil { - ps.logger.With("error", err).Error("Error transpiling module: " + mod.Name) + ps.logger.Error("Error transpiling module: " + mod.Name) return err } } if mod.Type == spec.ModuleTypeJavascript || mod.Type == spec.ModuleTypeTypescript { if program, err = goja.Compile(mod.Name, modPayload, true); err != nil { - ps.logger.With("error", err).Error("Error compiling module: " + mod.Name) + ps.logger.Error("Error compiling module: " + mod.Name) return err } } else { @@ -74,15 +80,16 @@ func (ps *ProxyState) setupModules() error { err = extractors.SetupModuleEventLoop(ps.printer, testRtCtx) if err != nil { ps.logger.Error("Error applying module changes", - "error", err, - "module", mod.Name, + zap.Error(err), zap.String("module", mod.Name), ) return err } ps.modPrograms.Insert(mod.Name+"/"+mod.Namespace.Name, program) elapsed := time.Since(start) - ps.logger.Debug("Module changed applied in "+elapsed.String(), - "name", mod.Name, "namespace", mod.Namespace.Name, + ps.logger.Debug("Module changed applied", + zap.Duration("elapsed", elapsed), + zap.String("name", mod.Name), + zap.String("namespace", mod.Namespace.Name), ) } } @@ -103,7 +110,7 @@ func (ps *ProxyState) setupRoutes() (err error) { ps.createModuleExtractorFunc(r), ) if err != nil { - ps.logger.With("error", err).Error("Error creating module buffer") + ps.logger.Error("Error creating module buffer", zap.Error(err)) return err } reqCtxProvider.SetModulePool(modPool) @@ -154,40 +161,41 @@ func (ps *ProxyState) createModuleExtractorFunc(r *spec.DGateRoute) ModuleExtrac // TODO: Perhaps have some entrypoint flag to determine which module to use m := r.Modules[0] if program, ok := ps.modPrograms.Find(m.Name + "/" + r.Namespace.Name); !ok { - ps.logger.Error("Error getting module program: invalid state") + ps.logger.Error("Error getting module program: invalid state", zap.Error(err)) return nil, fmt.Errorf("cannot find module program: %s/%s", m.Name, r.Namespace.Name) } else { rtCtx := NewRuntimeContext(ps, r, r.Modules...) if err := extractors.SetupModuleEventLoop(ps.printer, rtCtx, program); err != nil { - ps.logger.With("error", err).Error("Error creating runtime for route", - "route", reqCtx.route.Name, "namespace", reqCtx.route.Namespace.Name, + ps.logger.Error("Error creating runtime for route", + zap.String("route", reqCtx.route.Name), + zap.String("namespace", reqCtx.route.Namespace.Name), ) return nil, err } else { loop := rtCtx.EventLoop() errorHandler, err := extractors.ExtractErrorHandlerFunction(loop) if err != nil { - ps.logger.With("error", err).Error("Error extracting error handler function") + ps.logger.Error("Error extracting error handler function", zap.Error(err)) return nil, err } fetchUpstream, err := extractors.ExtractFetchUpstreamFunction(loop) if err != nil { - ps.logger.With("error", err).Error("Error extracting fetch upstream function") + ps.logger.Error("Error extracting fetch upstream function", zap.Error(err)) return nil, err } reqModifier, err := extractors.ExtractRequestModifierFunction(loop) if err != nil { - ps.logger.With("error", err).Error("Error extracting request modifier function") + ps.logger.Error("Error extracting request modifier function", zap.Error(err)) return nil, err } resModifier, err := extractors.ExtractResponseModifierFunction(loop) if err != nil { - ps.logger.With("error", err).Error("Error extracting response modifier function") + ps.logger.Error("Error extracting response modifier function", zap.Error(err)) return nil, err } reqHandler, err := extractors.ExtractRequestHandlerFunction(loop) if err != nil { - ps.logger.With("error", err).Error("Error extracting request handler function") + ps.logger.Error("Error extracting request handler function", zap.Error(err)) return nil, err } return NewModuleExtractor( @@ -200,52 +208,6 @@ func (ps *ProxyState) createModuleExtractorFunc(r *spec.DGateRoute) ModuleExtrac } } -// func (ps *ProxyState) startChangeLoop() { -// ps.proxyLock.Lock() -// if err := ps.reconfigureState(true, nil); err != nil { -// ps.logger.With("error", err).Error("Error initiating state") -// ps.Stop() -// return -// } -// ps.proxyLock.Unlock() - -// for { -// log := <-ps.changeChan -// switch log.Cmd { -// case spec.ShutdownCommand: -// ps.logger.Warn("Shutdown command received, closing change loop") -// log.PushError(nil) -// return -// case spec.RestartCommand: -// ps.logger.Warn("Restart command received, not supported") -// // ps.logger.Warn("Restart command received, restarting state") -// // go ps.restartState(func(err error) { -// // ps.logger.With("error", err).Error("Error restarting state") -// // os.Exit(1) -// // }) -// } - -// func() { -// ps.proxyLock.Lock() -// defer ps.proxyLock.Unlock() - -// err := ps.reconfigureState(false, log) -// if log.PushError(err); err != nil { -// ps.logger.With("error", err).Error( -// "Error reconfiguring state", -// "namespace", log.Namespace, -// ) -// go ps.restartState(func(err error) { -// ps.logger.With("error", err).Error("Error restarting state, exiting") -// ps.changeChan <- &spec.ChangeLog{ -// Cmd: spec.ShutdownCommand, -// } -// }) -// } -// }() -// } -// } - func (ps *ProxyState) startProxyServer() { cfg := ps.config.ProxyConfig hostPort := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) @@ -254,7 +216,7 @@ func (ps *ProxyState) startProxyServer() { server := &http.Server{ Addr: hostPort, Handler: ps, - ErrorLog: slog.NewLogLogger(proxyHttpLogger.Handler(), slog.LevelInfo), + ErrorLog: zap.NewStdLog(proxyHttpLogger), } if cfg.EnableHTTP2 { h2Server := &http2.Server{} @@ -267,7 +229,7 @@ func (ps *ProxyState) startProxyServer() { } } if err := server.ListenAndServe(); err != nil { - ps.logger.With("error", err).Error("Error starting proxy server") + ps.logger.Error("Error starting proxy server", zap.Error(err)) os.Exit(1) } } @@ -279,11 +241,11 @@ func (ps *ProxyState) startProxyServerTLS() { } hostPort := fmt.Sprintf("%s:%d", cfg.Host, cfg.TLS.Port) ps.logger.Info("Starting secure proxy server on " + hostPort) - proxyHttpsLogger := ps.logger.WithGroup("https-proxy") + proxyHttpsLogger := ps.logger.Named("https") secureServer := &http.Server{ Addr: hostPort, Handler: ps, - ErrorLog: slog.NewLogLogger(proxyHttpsLogger.Handler(), slog.LevelInfo), + ErrorLog: zap.NewStdLog(proxyHttpsLogger), TLSConfig: ps.DynamicTLSConfig( cfg.TLS.CertFile, cfg.TLS.KeyFile, @@ -300,18 +262,11 @@ func (ps *ProxyState) startProxyServerTLS() { } } if err := secureServer.ListenAndServeTLS("", ""); err != nil { - ps.logger.With("error", err).Error("Error starting secure proxy server") + ps.logger.Error("Error starting secure proxy server", zap.Error(err)) os.Exit(1) } } -func StartProxyGateway(version string, conf *config.DGateConfig) *ProxyState { - ps := NewProxyState(conf) - ps.version = version - - return ps -} - func (ps *ProxyState) Start() (err error) { defer func() { if err != nil { @@ -337,20 +292,19 @@ func (ps *ProxyState) Start() (err error) { } func (ps *ProxyState) Stop() { - cl := &spec.ChangeLog{ - Cmd: spec.ShutdownCommand, - } - done := make(chan error, 1) - cl.SetErrorChan(done) - // push change to change loop - select { - case ps.changeChan <- cl: - ps.logger.Debug("Shutdown command sent to change loop") - case <-time.After(5 * time.Second): - ps.logger.Warn("Timeout waiting for change loop to stop") + go func() { + <-time.After(10 * time.Second) + ps.logger.Error("Failed to stop proxy server") + os.Exit(1) + }() + ps.logger.Info("Stopping proxy server") + defer ps.proxyLock.Unlock() + ps.proxyLock.Lock() + ps.Logger().Sync() + if raftNode := ps.Raft(); raftNode != nil { + raftNode.Shutdown().Error() } - // wait for change loop to stop - <-done + os.Exit(0) } func (ps *ProxyState) HandleRoute(requestCtxProvider *RequestContextProvider, pattern string) http.HandlerFunc { diff --git a/internal/proxy/proxy_handler.go b/internal/proxy/proxy_handler.go index c1bd10e..6d02a51 100644 --- a/internal/proxy/proxy_handler.go +++ b/internal/proxy/proxy_handler.go @@ -8,6 +8,7 @@ import ( "time" "github.com/dgate-io/dgate/pkg/util" + "go.uber.org/zap" ) type ProxyHandlerFunc func(ps *ProxyState, reqCtx *RequestContext) @@ -22,13 +23,12 @@ func proxyHandler(ps *ProxyState, reqCtx *RequestContext) { event := ps.logger. With( - "route", reqCtx.route.Name, - "namespace", reqCtx.route.Namespace.Name, + zap.String("route", reqCtx.route.Name), + zap.String("namespace", reqCtx.route.Namespace.Name), ) if reqCtx.route.Service != nil { - event = event. - With("service", reqCtx.route.Service.Name) + event = event.With(zap.String("service", reqCtx.route.Service.Name)) } event.Debug("Request Log") }() @@ -81,14 +81,14 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt fetchUpstreamStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error fetching upstream") + ps.logger.Error("Error fetching upstream", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } host = hostUrl.String() } else { if reqCtx.route.Service.URLs == nil || len(reqCtx.route.Service.URLs) == 0 { - ps.logger.Error("Error getting service urls") + ps.logger.Error("Error getting service urls", zap.Any("service", reqCtx.route.Service)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } @@ -108,7 +108,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt } upstreamUrl, err := url.Parse(host) if err != nil { - ps.logger.With("error", err).Error("Error parsing upstream url") + ps.logger.Error("Error parsing upstream url", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusBadGateway) return } @@ -127,7 +127,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt resModifierStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error modifying response") + ps.logger.Error("Error modifying response", zap.Error(err)) return err } } @@ -135,8 +135,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt }). ErrorHandler(func(w http.ResponseWriter, r *http.Request, reqErr error) { upstreamErr = reqErr - ps.logger.With("error", reqErr). - Debug("Error proxying request") + ps.logger.Debug("Error proxying request", zap.Error(err)) // TODO: add metric for error if reqCtx.rw.HeadersSent() { return @@ -149,7 +148,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt errorHandlerStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error handling error") + ps.logger.Error("Error handling error", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } @@ -167,7 +166,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt reqModifierStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error modifying request") + ps.logger.Error("Error modifying request", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } @@ -175,7 +174,7 @@ func handleServiceProxy(ps *ProxyState, reqCtx *RequestContext, modExt ModuleExt rp, err := rpb.Build(upstreamUrl, reqCtx.pattern) if err != nil { - ps.logger.With("error", err).Error("Error creating reverse proxy") + ps.logger.Error("Error creating reverse proxy", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } @@ -202,7 +201,7 @@ func requestHandlerModule(ps *ProxyState, reqCtx *RequestContext, modExt ModuleE reqModifierStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error modifying request") + ps.logger.Error("Error modifying request", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } @@ -215,7 +214,7 @@ func requestHandlerModule(ps *ProxyState, reqCtx *RequestContext, modExt ModuleE requestHandlerStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error @ request_handler module") + ps.logger.Error("Error @ request_handler module", zap.Error(err)) if errorHandler, ok := modExt.ErrorHandlerFunc(); ok { // extract error handler function from module errorHandlerStart := time.Now() @@ -225,7 +224,7 @@ func requestHandlerModule(ps *ProxyState, reqCtx *RequestContext, modExt ModuleE errorHandlerStart, err, ) if err != nil { - ps.logger.With("error", err).Error("Error handling error") + ps.logger.Error("Error handling error", zap.Error(err)) util.WriteStatusCodeError(reqCtx.rw, http.StatusInternalServerError) return } diff --git a/internal/proxy/proxy_handler_test.go b/internal/proxy/proxy_handler_test.go index e934a8a..d2e7598 100644 --- a/internal/proxy/proxy_handler_test.go +++ b/internal/proxy/proxy_handler_test.go @@ -15,6 +15,7 @@ import ( "github.com/dgate-io/dgate/internal/proxy/proxytest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) // TODO: clean up the tests - make then simpler, more readable. @@ -26,7 +27,7 @@ func TestProxyHandler_ReverseProxy(t *testing.T) { // configtest.NewTest2DGateConfig(), } for _, conf := range configs { - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) rt, ok := ps.ResourceManager().GetRoute("test", "test") if !ok { @@ -87,7 +88,7 @@ func TestProxyHandler_ProxyHandler(t *testing.T) { // configtest.NewTest2DGateConfig(), } for _, conf := range configs { - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) ptBuilder := proxytest.CreateMockProxyTransportBuilder() ptBuilder.On("Retries", mock.Anything).Return(ptBuilder).Once() ptBuilder.On("Transport", mock.Anything).Return(ptBuilder).Once() @@ -144,7 +145,7 @@ func TestProxyHandler_ProxyHandlerError(t *testing.T) { // configtest.NewTest2DGateConfig(), } for _, conf := range configs { - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) ptBuilder := proxytest.CreateMockProxyTransportBuilder() ptBuilder.On("Retries", mock.Anything).Return(ptBuilder).Maybe() ptBuilder.On("Transport", mock.Anything).Return(ptBuilder).Maybe() diff --git a/internal/proxy/proxy_printer.go b/internal/proxy/proxy_printer.go index f6154fe..e37278d 100644 --- a/internal/proxy/proxy_printer.go +++ b/internal/proxy/proxy_printer.go @@ -1,10 +1,10 @@ package proxy -import "log/slog" +import "go.uber.org/zap" type ( ProxyPrinter struct { - logger *slog.Logger + logger *zap.Logger // logs []*printerLog } // printerLog struct { @@ -14,7 +14,7 @@ type ( // } ) -func NewProxyPrinter(logger *slog.Logger) *ProxyPrinter { +func NewProxyPrinter(logger *zap.Logger) *ProxyPrinter { return &ProxyPrinter{ logger: logger, // logs: make([]*printerLog, 0), diff --git a/internal/proxy/proxy_state.go b/internal/proxy/proxy_state.go index 7bcce03..f6d5c30 100644 --- a/internal/proxy/proxy_state.go +++ b/internal/proxy/proxy_state.go @@ -13,8 +13,6 @@ import ( "sync/atomic" "time" - "log/slog" - "github.com/dgate-io/dgate/internal/config" "github.com/dgate-io/dgate/internal/pattern" "github.com/dgate-io/dgate/internal/proxy/proxy_transport" @@ -32,6 +30,7 @@ import ( "github.com/dop251/goja" "github.com/dop251/goja_nodejs/console" "github.com/hashicorp/raft" + "go.uber.org/zap" ) type ProxyState struct { @@ -39,7 +38,7 @@ type ProxyState struct { debugMode bool startTime time.Time config *config.DGateConfig - logger *slog.Logger + logger *zap.Logger printer console.Printer store *proxystore.ProxyStore proxyLock *sync.RWMutex @@ -48,9 +47,8 @@ type ProxyState struct { metrics *ProxyMetrics sharedCache cache.TCache - changeChan chan *spec.ChangeLog - rm *resources.ResourceManager - skdr scheduler.Scheduler + rm *resources.ResourceManager + skdr scheduler.Scheduler providers avl.Tree[string, *RequestContextProvider] modPrograms avl.Tree[string, *goja.Program] @@ -66,20 +64,7 @@ type ProxyState struct { ProxyHandlerFunc ProxyHandlerFunc } -func NewProxyState(conf *config.DGateConfig) *ProxyState { - var logger *slog.Logger - logSource := util.EnvVarCheckBool("LOG_SOURCE") - if util.EnvVarCheckBool("LOG_JSON") { - logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ - Level: conf.LogLevel, AddSource: logSource, - })) - } else { - logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ - Level: conf.LogLevel, AddSource: logSource, - })) - } - slog.SetDefault(logger) - +func NewProxyState(logger *zap.Logger, conf *config.DGateConfig) *ProxyState { var dataStore storage.Storage switch conf.Storage.StorageType { case config.StorageTypeDebug: @@ -110,26 +95,25 @@ func NewProxyState(conf *config.DGateConfig) *ProxyState { if conf.ProxyConfig.EnableConsoleLogger { printer = NewProxyPrinter(logger) } - rpLogger := logger.WithGroup("reverse-proxy") - storeLogger := logger.WithGroup("store") - schedulerLogger := logger.WithGroup("scheduler") + rpLogger := logger.Named("reverse-proxy") + storeLogger := logger.Named("store") + schedulerLogger := logger.Named("scheduler") replicationEnabled := false if conf.AdminConfig != nil && conf.AdminConfig.Replication != nil { replicationEnabled = true } state := &ProxyState{ - version: "unknown", - startTime: time.Now(), - raftReady: atomic.Bool{}, - logger: logger, - debugMode: conf.Debug, - config: conf, - metrics: NewProxyMetrics(), - printer: printer, - routers: avl.NewTree[string, *router.DynamicRouter](), - changeChan: make(chan *spec.ChangeLog, 1), - rm: resources.NewManager(opt), + version: "unknown", + startTime: time.Now(), + raftReady: atomic.Bool{}, + logger: logger, + debugMode: conf.Debug, + config: conf, + metrics: NewProxyMetrics(), + printer: printer, + routers: avl.NewTree[string, *router.DynamicRouter](), + rm: resources.NewManager(opt), skdr: scheduler.New(scheduler.Options{ Logger: schedulerLogger, }), @@ -141,7 +125,7 @@ func NewProxyState(conf *config.DGateConfig) *ProxyState { replicationEnabled: replicationEnabled, ReverseProxyBuilder: reverse_proxy.NewBuilder(). FlushInterval(-1). - ErrorLogger(slog.NewLogLogger(rpLogger.Handler(), slog.LevelInfo)). + ErrorLogger(zap.NewStdLog(rpLogger)). CustomRewrite(func(in *http.Request, out *http.Request) { if in.URL.Scheme == "ws" { out.URL.Scheme = "http" @@ -176,7 +160,7 @@ func (ps *ProxyState) Store() *proxystore.ProxyStore { return ps.store } -func (ps *ProxyState) Logger() *slog.Logger { +func (ps *ProxyState) Logger() *zap.Logger { return ps.logger } @@ -215,12 +199,8 @@ func (ps *ProxyState) SetupRaft(r *raft.Raft, rc *raft.Config) { func (ps *ProxyState) WaitForChanges() error { ps.proxyLock.RLock() - if len(ps.changeChan) > 0 { - ps.proxyLock.RUnlock() - return <-ps.applyChange(nil) - } defer ps.proxyLock.RUnlock() - return nil + return <-ps.applyChange(nil) } func (ps *ProxyState) ApplyChangeLog(log *spec.ChangeLog) error { @@ -242,7 +222,8 @@ func (ps *ProxyState) ApplyChangeLog(log *spec.ChangeLog) error { future := r.ApplyLog(raftLog, time.Second*15) ps.logger.With(). Debug("waiting for reply from raft", - "changelog", log, "id", log.ID, + zap.String("id", log.ID), + zap.Stringer("command", log.Cmd), ) return future.Error() } else { @@ -268,6 +249,8 @@ func (ps *ProxyState) restartState(errFn func(error)) { ps.proxyLock.Lock() defer ps.proxyLock.Unlock() + ps.logger.Info("Attempting to restart state...") + ps.rm.Empty() ps.modPrograms.Clear() ps.providers.Clear() @@ -287,7 +270,7 @@ func (ps *ProxyState) restartState(errFn func(error)) { } } if err := ps.restoreFromChangeLogs(true); err != nil { - errFn(err) + go errFn(err) return } ps.logger.Info("State successfully restarted") @@ -314,8 +297,7 @@ func (ps *ProxyState) ReloadState(check bool, logs ...*spec.ChangeLog) error { func (ps *ProxyState) ProcessChangeLog(log *spec.ChangeLog, reload bool) error { err := ps.processChangeLog(log, reload, !ps.replicationEnabled) if err != nil { - ps.logger.With("error", err). - Error("processing error") + ps.logger.Error("processing error", zap.Error(err)) } return err } @@ -364,8 +346,7 @@ func (ps *ProxyState) getDomainCertificate(domain string) (*tls.Certificate, err _, domainMatch, err := pattern.MatchAnyPattern(domain, allowedDomains) if err != nil { ps.logger.Error("Error checking domain match list", - "error", - err.Error(), + zap.Error(err), ) return nil, err } @@ -376,8 +357,7 @@ func (ps *ProxyState) getDomainCertificate(domain string) (*tls.Certificate, err _, match, err := pattern.MatchAnyPattern(domain, d.Patterns) if err != nil { ps.logger.Error("Error checking domain match list", - "error", - err.Error(), + zap.Error(err), ) return nil, err } else if match && d.Cert != "" && d.Key != "" { @@ -396,9 +376,9 @@ func (ps *ProxyState) getDomainCertificate(domain string) (*tls.Certificate, err []byte(d.Cert), []byte(d.Key)) if err != nil { ps.logger.Error("Error loading cert", - "error", err.Error(), - "domain_name", d.Name, - "namespace", d.Namespace.Name, + zap.Error(err), + zap.String("domain_name", d.Name), + zap.String("namespace", d.Namespace.Name), ) return nil, err } @@ -521,7 +501,7 @@ func (ps *ProxyState) FindNamespaceByRequest(r *http.Request) *spec.DGateNamespa } _, match, err := pattern.MatchAnyPattern(host, d.Patterns) if err != nil { - ps.logger.With("error", err).Error("error matching namespace") + ps.logger.Error("error matching namespace", zap.Error(err)) } else if match { return d.Namespace } @@ -555,12 +535,12 @@ func (ps *ProxyState) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, ok, err = pattern.MatchAnyPattern(host, allowedDomains) if err != nil { ps.logger.Debug("Error checking domain match list", - "error", err.Error(), + zap.Error(err), ) util.WriteStatusCodeError(w, http.StatusInternalServerError) return } else if !ok { - ps.logger.Debug("Domain not allowed", "domain", host) + ps.logger.Debug("Domain not allowed", zap.String("domain", host)) // if debug mode is enabled, return a 403 util.WriteStatusCodeError(w, http.StatusForbidden) if ps.debugMode { @@ -573,7 +553,7 @@ func (ps *ProxyState) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.TLS == nil && len(redirectDomains) > 0 { if _, ok, err = pattern.MatchAnyPattern(host, redirectDomains); err != nil { ps.logger.Error("Error checking domain match list", - "error", err.Error(), + zap.Error(err), ) util.WriteStatusCodeError(w, http.StatusInternalServerError) return @@ -581,7 +561,7 @@ func (ps *ProxyState) ServeHTTP(w http.ResponseWriter, r *http.Request) { url := *r.URL url.Scheme = "https" ps.logger.Info("Redirecting to https", - "", url.String(), + zap.Stringer("url", &url), ) http.Redirect(w, r, url.String(), // maybe change to http.StatusMovedPermanently @@ -593,18 +573,17 @@ func (ps *ProxyState) ServeHTTP(w http.ResponseWriter, r *http.Request) { router.ServeHTTP(w, r) } else { ps.logger.Debug("No router found for namespace", - "namespace", ns.Name, + zap.String("namespace", ns.Name), ) util.WriteStatusCodeError(w, http.StatusNotFound) } } else { - ps.logger.Debug( - "No namespace found for request", - "protocol", r.Proto, - "host", r.Host, - "path", r.URL.Path, - "secure", r.TLS != nil, - "remote_addr", r.RemoteAddr, + ps.logger.Debug("No namespace found for request", + zap.String("protocol", r.Proto), + zap.String("host", r.Host), + zap.String("path", r.URL.Path), + zap.Bool("secure", r.TLS != nil), + zap.String("remote_addr", r.RemoteAddr), ) util.WriteStatusCodeError(w, http.StatusNotFound) } diff --git a/internal/proxy/proxy_state_test.go b/internal/proxy/proxy_state_test.go index 37aa57c..0a3d27d 100644 --- a/internal/proxy/proxy_state_test.go +++ b/internal/proxy/proxy_state_test.go @@ -10,6 +10,7 @@ import ( "github.com/dgate-io/dgate/internal/proxy" "github.com/dgate-io/dgate/pkg/spec" "github.com/stretchr/testify/assert" + "go.uber.org/zap" ) // Raft Test -> ApplyChangeLog, WaitForChanges, @@ -19,7 +20,7 @@ import ( func TestDynamicTLSConfig_DomainCert(t *testing.T) { conf := configtest.NewTestDGateConfig_DomainAndNamespaces() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) tlsConfig := ps.DynamicTLSConfig("", "") clientHello := &tls.ClientHelloInfo{ @@ -36,7 +37,7 @@ func TestDynamicTLSConfig_DomainCert(t *testing.T) { func TestDynamicTLSConfig_DomainCertCache(t *testing.T) { conf := configtest.NewTestDGateConfig_DomainAndNamespaces() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) d := ps.ResourceManager().GetDomainsByPriority()[0] key := fmt.Sprintf("cert:%s:%s:%d", d.Namespace.Name, d.Name, d.CreatedAt.UnixMilli()) @@ -64,7 +65,7 @@ func TestDynamicTLSConfig_DomainCertCache(t *testing.T) { func TestDynamicTLSConfig_Fallback(t *testing.T) { conf := configtest.NewTestDGateConfig_DomainAndNamespaces() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) tlsConfig := ps.DynamicTLSConfig("testdata/server.crt", "testdata/server.key") // this should have a match that is not the fallback @@ -93,7 +94,7 @@ func TestDynamicTLSConfig_Fallback(t *testing.T) { func TestFindNamespaceByRequest_OneNamespaceNoDomain(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -117,7 +118,7 @@ func TestFindNamespaceByRequest_OneNamespaceNoDomain(t *testing.T) { func TestFindNamespaceByRequest_DomainsAndNamespaces(t *testing.T) { conf := configtest.NewTestDGateConfig_DomainAndNamespaces() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -144,7 +145,7 @@ func TestFindNamespaceByRequest_DomainsAndNamespaces(t *testing.T) { } func TestFindNamespaceByRequest_DomainsAndNamespacesDefault(t *testing.T) { conf := configtest.NewTestDGateConfig_DomainAndNamespaces2() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -174,7 +175,7 @@ func TestFindNamespaceByRequest_DomainsAndNamespacesDefault(t *testing.T) { // func TestApplyChangeLog(t *testing.T) { // conf := configtest.NewTestDGateConfig() -// ps := proxy.NewProxyState(conf) +// ps := proxy.NewProxyState(zap.NewNop(), conf) // if err := ps.Store().InitStore(); err != nil { // t.Fatal(err) // } @@ -184,7 +185,7 @@ func TestFindNamespaceByRequest_DomainsAndNamespacesDefault(t *testing.T) { func TestProcessChangeLog_RMSecrets(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -226,7 +227,7 @@ func TestProcessChangeLog_RMSecrets(t *testing.T) { func TestProcessChangeLog_Route(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -267,7 +268,7 @@ func TestProcessChangeLog_Route(t *testing.T) { func TestProcessChangeLog_Service(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -302,7 +303,7 @@ func TestProcessChangeLog_Service(t *testing.T) { func TestProcessChangeLog_Module(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -336,7 +337,7 @@ func TestProcessChangeLog_Module(t *testing.T) { } func TestProcessChangeLog_Namespace(t *testing.T) { - ps := proxy.NewProxyState(configtest.NewTestDGateConfig()) + ps := proxy.NewProxyState(zap.NewNop(), configtest.NewTestDGateConfig()) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -365,7 +366,7 @@ func TestProcessChangeLog_Namespace(t *testing.T) { func TestProcessChangeLog_Collection(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -402,7 +403,7 @@ func TestProcessChangeLog_Collection(t *testing.T) { func TestProcessChangeLog_Document(t *testing.T) { conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) if err := ps.Store().InitStore(); err != nil { t.Fatal(err) } @@ -411,8 +412,8 @@ func TestProcessChangeLog_Document(t *testing.T) { Name: "test", NamespaceName: "test", // Type: spec.CollectionTypeDocument, - Visibility: spec.CollectionVisibilityPrivate, - Tags: []string{"test"}, + Visibility: spec.CollectionVisibilityPrivate, + Tags: []string{"test"}, } cl := spec.NewChangeLog(c, c.NamespaceName, spec.AddCollectionCommand) diff --git a/internal/proxy/proxystore/proxy_store.go b/internal/proxy/proxystore/proxy_store.go index 2431935..430d3c5 100644 --- a/internal/proxy/proxystore/proxy_store.go +++ b/internal/proxy/proxystore/proxy_store.go @@ -2,7 +2,6 @@ package proxystore import ( "encoding/json" - "log/slog" "time" "errors" @@ -10,14 +9,15 @@ import ( "github.com/dgate-io/dgate/pkg/spec" "github.com/dgate-io/dgate/pkg/storage" "github.com/dgraph-io/badger/v4" + "go.uber.org/zap" ) type ProxyStore struct { storage storage.Storage - logger *slog.Logger + logger *zap.Logger } -func New(storage storage.Storage, logger *slog.Logger) *ProxyStore { +func New(storage storage.Storage, logger *zap.Logger) *ProxyStore { return &ProxyStore{ storage: storage, logger: logger, @@ -43,13 +43,13 @@ func (store *ProxyStore) FetchChangeLogs() ([]*spec.ChangeLog, error) { if len(clBytes) == 0 { return nil, nil } - store.logger.Debug("found changelog entries", "numBytes", len(clBytes)) + store.logger.Debug("found changelog entries", zap.Int("numBytes", len(clBytes))) logs := make([]*spec.ChangeLog, len(clBytes)) for i, clKv := range clBytes { var clObj spec.ChangeLog err = json.Unmarshal(clKv.Value, &clObj) if err != nil { - store.logger.Debug("failed to unmarshal changelog entry", "error", err.Error()) + store.logger.Debug("failed to unmarshal changelog entry", zap.Error(err)) return nil, errors.New("failed to unmarshal changelog entry: " + err.Error()) } logs[i] = &clObj @@ -68,8 +68,9 @@ RETRY: err = store.storage.Set("changelog/"+cl.ID, clBytes) if err != nil { if retries > 0 { - store.logger.With("error", err). - Error("failed to store changelog", "retries", retries) + store.logger.Error("failed to store changelog", + zap.Error(err), zap.Int("retries", retries), + ) time.Sleep(delay) retries-- goto RETRY @@ -106,7 +107,8 @@ func (store *ProxyStore) FetchDocument(nsName, colName, docId string) (*spec.Doc doc := &spec.Document{} err = json.Unmarshal(docBytes, doc) if err != nil { - store.logger.Debug("failed to unmarshal document entry: %s, skipping %s", err.Error(), docId) + store.logger.Debug("failed to unmarshal document entry: %s, skipping %s", + zap.Error(err), zap.String("document_id", docId)) return nil, errors.New("failed to unmarshal document entry" + err.Error()) } return doc, nil diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 356805b..520f255 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -3,12 +3,12 @@ package cache import ( "context" "errors" - "log/slog" "sync" "time" "github.com/dgate-io/dgate/pkg/scheduler" "github.com/dgate-io/dgate/pkg/util/heap" + "go.uber.org/zap" ) type TCache interface { @@ -59,7 +59,7 @@ type cacheEntry struct { type CacheOptions struct { CheckInterval time.Duration - Logger *slog.Logger + Logger *zap.Logger } var ( diff --git a/pkg/modules/extractors/runtime_test.go b/pkg/modules/extractors/runtime_test.go index a290e82..068d812 100644 --- a/pkg/modules/extractors/runtime_test.go +++ b/pkg/modules/extractors/runtime_test.go @@ -11,6 +11,7 @@ import ( "github.com/dgate-io/dgate/pkg/typescript" "github.com/dop251/goja" "github.com/dop251/goja_nodejs/console" + "go.uber.org/zap" ) const TS_PAYLOAD_CUSTOMFUNC = ` @@ -103,7 +104,7 @@ func TestPrinter(t *testing.T) { cp := &consolePrinter{make(map[string]int)} rt := &spec.DGateRoute{Namespace: &spec.DGateNamespace{}} conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) rtCtx := proxy.NewRuntimeContext(ps, rt) if err := extractors.SetupModuleEventLoop( cp, rtCtx, program, @@ -126,7 +127,7 @@ func TestPrinter(t *testing.T) { func BenchmarkNewModuleRuntime(b *testing.B) { program := testutil.CreateTSProgram(b, TS_PAYLOAD_CUSTOMFUNC) conf := configtest.NewTestDGateConfig() - ps := proxy.NewProxyState(conf) + ps := proxy.NewProxyState(zap.NewNop(), conf) b.ResetTimer() b.Run("CreateModuleRuntime", func(b *testing.B) { diff --git a/pkg/raftadmin/raftadmin.go b/pkg/raftadmin/raftadmin.go index 8b2b17a..168424b 100644 --- a/pkg/raftadmin/raftadmin.go +++ b/pkg/raftadmin/raftadmin.go @@ -12,9 +12,8 @@ import ( "sync" "time" - "log/slog" - "github.com/hashicorp/raft" + "go.uber.org/zap" ) // RaftAdminHTTPServer provides a HTTP-based transport that can be used to @@ -22,14 +21,14 @@ import ( // application is an HTTP server already and you do not want to use multiple // different transports (if not, you can use raft.NetworkTransport). type RaftAdminHTTPServer struct { - logger *slog.Logger + logger *zap.Logger r *raft.Raft // addrs map[raft.ServerID]raft.ServerAddress addrs []raft.ServerAddress } // NewRaftAdminHTTPServer creates a new HTTP transport on the given addr. -func NewRaftAdminHTTPServer(r *raft.Raft, logger *slog.Logger, addrs []raft.ServerAddress) *RaftAdminHTTPServer { +func NewRaftAdminHTTPServer(r *raft.Raft, logger *zap.Logger, addrs []raft.ServerAddress) *RaftAdminHTTPServer { return &RaftAdminHTTPServer{ logger: logger, r: r, @@ -293,9 +292,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "Barrier": @@ -315,9 +313,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque } res.Header().Set("X-Raft-Index", fmt.Sprintf("%d", resp.Index)) res.WriteHeader(http.StatusAccepted) - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "DemoteVoter": @@ -340,9 +337,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "LastContact": @@ -351,9 +347,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "LastIndex": @@ -362,9 +357,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "Leader": @@ -373,9 +367,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "LeadershipTransfer": @@ -436,9 +429,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "Stats": @@ -447,9 +439,8 @@ func (t *RaftAdminHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Reque http.Error(res, err.Error(), http.StatusInternalServerError) return } - err = json.NewEncoder(res).Encode(resp) - if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + if err = json.NewEncoder(res).Encode(resp); err != nil { + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } return case "VerifyLeader": @@ -490,6 +481,6 @@ func (t *RaftAdminHTTPServer) genericResponse(req *http.Request, res http.Respon res.WriteHeader(http.StatusAccepted) err = json.NewEncoder(res).Encode(resp) if err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + t.logger.Error("error occurred when handling command", zap.String("command", cmd), zap.Error(err)) } } diff --git a/pkg/raftadmin/raftadmin_client.go b/pkg/raftadmin/raftadmin_client.go index 0f014f0..905dfd2 100644 --- a/pkg/raftadmin/raftadmin_client.go +++ b/pkg/raftadmin/raftadmin_client.go @@ -10,9 +10,8 @@ import ( "strings" "time" - "log/slog" - "github.com/hashicorp/raft" + "go.uber.org/zap" ) type Doer func(*http.Request) (*http.Response, error) @@ -20,10 +19,10 @@ type Doer func(*http.Request) (*http.Response, error) type HTTPAdminClient struct { do Doer urlFmt string - logger *slog.Logger + logger *zap.Logger } -func NewHTTPAdminClient(doer Doer, urlFmt string, logger *slog.Logger) *HTTPAdminClient { +func NewHTTPAdminClient(doer Doer, urlFmt string, logger *zap.Logger) *HTTPAdminClient { if doer == nil { doer = http.DefaultClient.Do } diff --git a/pkg/raftadmin/raftadmin_test.go b/pkg/raftadmin/raftadmin_test.go index 1e2573e..b4eca87 100644 --- a/pkg/raftadmin/raftadmin_test.go +++ b/pkg/raftadmin/raftadmin_test.go @@ -3,17 +3,17 @@ package raftadmin import ( "context" "io" - "log/slog" "net/http" "net/http/httptest" "strings" "testing" "time" - "github.com/dgate-io/dgate/pkg/util/logger" + "github.com/dgate-io/dgate/pkg/util/logadapter" "github.com/hashicorp/raft" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) type MockTransport struct { @@ -93,12 +93,9 @@ func (m *MockFSM) Restore(io.ReadCloser) error { } func setupRaftAdmin(t *testing.T) *httptest.Server { - lgr := slog.New(slog.NewTextHandler(io.Discard, nil)) - raftConfig := raft.DefaultConfig() raftConfig.LocalID = "1" - raftConfig.Logger = logger.NewSLogHCAdapter(lgr) - + raftConfig.Logger = logadapter.NewZap2HCLogAdapter(zap.NewNop()) mockFSM := &MockFSM{} mockFSM.On("Apply", mock.Anything).Return(nil) @@ -133,7 +130,7 @@ func setupRaftAdmin(t *testing.T) *httptest.Server { <-time.After(time.Second * 5) raftAdmin := NewRaftAdminHTTPServer( - raftNode, lgr, + raftNode, zap.NewNop(), []raft.ServerAddress{ "localhost:9090", }, @@ -175,7 +172,7 @@ func TestRaft(t *testing.T) { client := NewHTTPAdminClient( server.Client().Do, "http://(address)/raftadmin", - slog.Default(), + zap.NewNop(), ) serverAddr := raft.ServerAddress(server.Listener.Addr().String()) leader, err := client.Leader(ctx, serverAddr) diff --git a/pkg/rafthttp/rafthttp.go b/pkg/rafthttp/rafthttp.go index a049e1b..d01ea35 100644 --- a/pkg/rafthttp/rafthttp.go +++ b/pkg/rafthttp/rafthttp.go @@ -5,13 +5,13 @@ import ( "encoding/json" "fmt" "io" - "log/slog" "net/http" "path" "strings" "time" "github.com/hashicorp/raft" + "go.uber.org/zap" ) // Doer provides the Do() method, as found in net/http.Client. @@ -28,7 +28,7 @@ type Doer interface { // application is an HTTP server already and you do not want to use multiple // different transports (if not, you can use raft.NetworkTransport). type HTTPTransport struct { - logger *slog.Logger + logger *zap.Logger consumer chan raft.RPC addr raft.ServerAddress client Doer @@ -37,7 +37,7 @@ type HTTPTransport struct { var _ raft.Transport = (*HTTPTransport)(nil) -func NewHTTPTransport(addr raft.ServerAddress, client Doer, logger *slog.Logger, urlFmt string) *HTTPTransport { +func NewHTTPTransport(addr raft.ServerAddress, client Doer, logger *zap.Logger, urlFmt string) *HTTPTransport { if client == nil { client = http.DefaultClient } @@ -265,6 +265,7 @@ func (t *HTTPTransport) ServeHTTP(res http.ResponseWriter, req *http.Request) { if resp.Error != nil { err := fmt.Errorf("could not run RPC: %v", resp.Error) + t.logger.Error("error running RPC", zap.Error(err)) http.Error(res, err.Error(), http.StatusBadRequest) return } @@ -289,7 +290,7 @@ func (t *HTTPTransport) ServeHTTP(res http.ResponseWriter, req *http.Request) { } if err := t.handle(res, req, rpc); err != nil { - t.logger.Info("[%s, %s] %v\n", req.RemoteAddr, cmd, err) + t.logger.Info("Handling command", zap.String("command", cmd), zap.Error(err)) } } diff --git a/pkg/rafthttp/rafthttp_test.go b/pkg/rafthttp/rafthttp_test.go index 728388e..6656096 100644 --- a/pkg/rafthttp/rafthttp_test.go +++ b/pkg/rafthttp/rafthttp_test.go @@ -8,12 +8,11 @@ import ( "testing" "time" - "log/slog" - "github.com/dgate-io/dgate/pkg/rafthttp" - "github.com/dgate-io/dgate/pkg/util/logger" + "github.com/dgate-io/dgate/pkg/util/logadapter" "github.com/hashicorp/raft" "github.com/stretchr/testify/mock" + "go.uber.org/zap" ) type MockFSM struct { @@ -44,9 +43,8 @@ func TestExample(t *testing.T) { } log.Printf("Listening on %s", ln.Addr().String()) srvAddr := raft.ServerAddress(ln.Addr().String()) - lgr := slog.New(slog.NewTextHandler(io.Discard, nil)) transport := rafthttp.NewHTTPTransport( - srvAddr, http.DefaultClient, lgr, + srvAddr, http.DefaultClient, zap.NewNop(), "http://(address)/raft", ) srv := &http.Server{ @@ -56,7 +54,8 @@ func TestExample(t *testing.T) { raftConfig := raft.DefaultConfig() raftConfig.LocalID = "1" - raftConfig.Logger = logger.NewSLogHCAdapter(lgr) + raftConfig.Logger = logadapter. + NewZap2HCLogAdapter(zap.NewNop()) mockFSM := &MockFSM{} logStore := raft.NewInmemStore() diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index 8d01f75..a891e1c 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -3,11 +3,11 @@ package scheduler import ( "context" "errors" - "log/slog" "sync" "time" "github.com/dgate-io/dgate/pkg/util/heap" + "go.uber.org/zap" ) type ( @@ -45,7 +45,7 @@ type scheduler struct { opts Options ctx context.Context cancel context.CancelFunc - logger *slog.Logger + logger *zap.Logger tasks map[string]*TaskDefinition pendingJobs priorityQueue mutex *sync.RWMutex @@ -74,7 +74,7 @@ var ( type Options struct { Interval time.Duration - Logger *slog.Logger + Logger *zap.Logger AutoRun bool } @@ -161,7 +161,8 @@ func (s *scheduler) executeTask(tdt time.Time, taskDef *TaskDefinition) { s.pendingJobs.Push(µs, taskDef) } if r := recover(); r != nil { - s.logger.Error("panic occurred while executing task %s: %v", taskDef.Name, r) + s.logger.Error("panic occurred while executing task", + zap.String("task_name", taskDef.Name), zap.Any("error", r)) } }() taskDef.Func(taskDef.ctx) diff --git a/pkg/spec/change_log.go b/pkg/spec/change_log.go index 6c4618a..b815aec 100644 --- a/pkg/spec/change_log.go +++ b/pkg/spec/change_log.go @@ -41,6 +41,9 @@ func (cl *ChangeLog) SetErrorChan(errChan chan error) { } func (cl *ChangeLog) PushError(err error) { + if cl == nil { + return + } if cl.errChan != nil { cl.errChan <- err } @@ -84,9 +87,7 @@ var ( DeleteSecretCommand Command = newCommand(Delete, Secrets) // internal commands - NoopCommand Command = Command("noop") - ShutdownCommand Command = Command("shutdown") - RestartCommand Command = Command("restart") + NoopCommand Command = Command("noop") ) func newCommand(action Action, resource Resource) Command { diff --git a/pkg/storage/badger_logger.go b/pkg/storage/badger_logger.go index 857bd12..39b4188 100644 --- a/pkg/storage/badger_logger.go +++ b/pkg/storage/badger_logger.go @@ -2,13 +2,13 @@ package storage import ( "fmt" - "log/slog" "github.com/dgraph-io/badger/v4" + "go.uber.org/zap" ) type badgerLoggerAdapter struct { - logger *slog.Logger + logger *zap.Logger } func (b *badgerLoggerAdapter) Errorf(format string, args ...any) { @@ -27,9 +27,8 @@ func (b *badgerLoggerAdapter) Debugf(format string, args ...any) { b.logger.Debug(fmt.Sprintf(format, args...)) } -func newBadgerLoggerAdapter(component string, logger *slog.Logger) badger.Logger { - logger = logger.WithGroup(component) +func newBadgerLoggerAdapter(component string, logger *zap.Logger) badger.Logger { return &badgerLoggerAdapter{ - logger: logger, + logger: logger.Named(component), } } diff --git a/pkg/storage/debug_storage.go b/pkg/storage/debug_storage.go index 2fc53e8..e74eb0b 100644 --- a/pkg/storage/debug_storage.go +++ b/pkg/storage/debug_storage.go @@ -4,13 +4,12 @@ import ( "errors" "strings" - "log/slog" - "github.com/dgate-io/dgate/pkg/util/tree/avl" + "go.uber.org/zap" ) type DebugStoreConfig struct { - Logger *slog.Logger + Logger *zap.Logger } type DebugStore struct { diff --git a/pkg/storage/file_storage.go b/pkg/storage/file_storage.go index efa68ab..3fd417b 100644 --- a/pkg/storage/file_storage.go +++ b/pkg/storage/file_storage.go @@ -6,15 +6,14 @@ import ( "os" "strings" - "log/slog" - "github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4/options" + "go.uber.org/zap" ) type FileStoreConfig struct { Directory string `koanf:"dir"` - Logger *slog.Logger + Logger *zap.Logger } type FileStore struct { diff --git a/pkg/storage/memory_storage.go b/pkg/storage/memory_storage.go index b593356..722e774 100644 --- a/pkg/storage/memory_storage.go +++ b/pkg/storage/memory_storage.go @@ -1,12 +1,12 @@ package storage -import "log/slog" +import "go.uber.org/zap" type MemoryStoreConfig struct { // Path to the directory where the files will be stored. // If the directory does not exist, it will be created. // If the directory exists, it will be used. - Logger *slog.Logger + Logger *zap.Logger } type MemoryStore struct { diff --git a/pkg/util/logadapter/zap2badger.go b/pkg/util/logadapter/zap2badger.go new file mode 100644 index 0000000..0266058 --- /dev/null +++ b/pkg/util/logadapter/zap2badger.go @@ -0,0 +1,36 @@ +package logadapter + +import ( + "fmt" + + "github.com/dgraph-io/badger/v4" + "go.uber.org/zap" +) + +type Zap2BadgerAdapter struct { + logger *zap.Logger +} + +var _ badger.Logger = (*Zap2BadgerAdapter)(nil) + +func NewZap2BadgerAdapter(logger *zap.Logger) *Zap2BadgerAdapter { + return &Zap2BadgerAdapter{ + logger: logger, + } +} + +func (a *Zap2BadgerAdapter) Debugf(format string, args ...interface{}) { + a.logger.Debug(fmt.Sprintf(format, args...)) +} + +func (a *Zap2BadgerAdapter) Infof(format string, args ...interface{}) { + a.logger.Info(fmt.Sprintf(format, args...)) +} + +func (a *Zap2BadgerAdapter) Warningf(format string, args ...interface{}) { + a.logger.Warn(fmt.Sprintf(format, args...)) +} + +func (a *Zap2BadgerAdapter) Errorf(format string, args ...interface{}) { + a.logger.Error(fmt.Sprintf(format, args...)) +} diff --git a/pkg/util/logadapter/zap2hc.go b/pkg/util/logadapter/zap2hc.go new file mode 100644 index 0000000..2fa81bd --- /dev/null +++ b/pkg/util/logadapter/zap2hc.go @@ -0,0 +1,135 @@ +package logadapter + +import ( + "context" + "io" + "log" + + "github.com/hashicorp/go-hclog" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type Zap2HCLogAdapter struct { + ctx context.Context + logger *zap.Logger +} + +func NewZap2HCLogAdapter(logger *zap.Logger) *Zap2HCLogAdapter { + return &Zap2HCLogAdapter{context.Background(), logger} +} + +func (l *Zap2HCLogAdapter) IsTrace() bool { + return l.logger.Core().Enabled(hc2zapLevel(hclog.Trace)) +} + +func (l *Zap2HCLogAdapter) IsDebug() bool { + return l.logger.Core().Enabled(hc2zapLevel(hclog.Debug)) +} + +func (l *Zap2HCLogAdapter) IsInfo() bool { + return l.logger.Core().Enabled(hc2zapLevel(hclog.Info)) +} + +func (l *Zap2HCLogAdapter) IsWarn() bool { + return l.logger.Core().Enabled(hc2zapLevel(hclog.Warn)) +} + +func (l *Zap2HCLogAdapter) IsError() bool { + return l.logger.Core().Enabled(hc2zapLevel(hclog.Error)) +} + +func (l *Zap2HCLogAdapter) Trace(format string, args ...interface{}) {} + +func (l *Zap2HCLogAdapter) Debug(format string, args ...interface{}) { + l.logger.Debug(format) +} + +func (l *Zap2HCLogAdapter) Info(format string, args ...interface{}) { + l.logger.Info(format) +} + +func (l *Zap2HCLogAdapter) Warn(format string, args ...interface{}) { + l.logger.Warn(format) +} + +func (l *Zap2HCLogAdapter) Error(format string, args ...interface{}) { + l.logger.Error(format) +} + +func (l *Zap2HCLogAdapter) Log(level hclog.Level, format string, args ...interface{}) { + switch level { + case hclog.Debug: + l.Debug(format, args...) + case hclog.Info: + l.Info(format, args...) + case hclog.Warn: + l.Warn(format, args...) + case hclog.Error: + l.Error(format, args...) + } +} + +func (l *Zap2HCLogAdapter) GetLevel() hclog.Level { + return zap2hcLogLevel(l.logger.Level()) +} + +func (l *Zap2HCLogAdapter) SetLevel(level hclog.Level) {} + +func (l *Zap2HCLogAdapter) Name() string { + return l.logger.Name() +} + +func (l *Zap2HCLogAdapter) Named(name string) hclog.Logger { + return &Zap2HCLogAdapter{l.ctx, l.logger.Named(name)} +} + +func (l *Zap2HCLogAdapter) ResetNamed(name string) hclog.Logger { + return &Zap2HCLogAdapter{l.ctx, l.logger.Named(name)} +} + +func (l *Zap2HCLogAdapter) With(args ...any) hclog.Logger { + return &Zap2HCLogAdapter{l.ctx, l.logger.Sugar().With(args...).Desugar()} +} + +func (l *Zap2HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { + return zap.NewStdLog(l.logger) +} + +func (l *Zap2HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { + return l.StandardLogger(opts).Writer() +} + +func (l *Zap2HCLogAdapter) ImpliedArgs() []interface{} { + return nil +} + +func hc2zapLevel(lvl hclog.Level) zapcore.Level { + switch lvl { + case hclog.Debug, hclog.Trace, hclog.NoLevel: + return zap.DebugLevel + case hclog.Info, hclog.DefaultLevel: + return zap.InfoLevel + case hclog.Warn: + return zap.WarnLevel + case hclog.Error, hclog.Off: + return zap.ErrorLevel + default: + return zap.InfoLevel + } +} + +func zap2hcLogLevel(lvl zapcore.Level) hclog.Level { + switch lvl { + case zap.DebugLevel: + return hclog.Debug + case zap.InfoLevel: + return hclog.Info + case zap.WarnLevel: + return hclog.Warn + case zap.ErrorLevel, zap.PanicLevel, zap.DPanicLevel, zap.FatalLevel: + return hclog.Error + default: + return hclog.NoLevel + } +} diff --git a/pkg/util/logger/logging.go b/pkg/util/logger/logging.go deleted file mode 100644 index aba93e0..0000000 --- a/pkg/util/logger/logging.go +++ /dev/null @@ -1,135 +0,0 @@ -package logger - -import ( - "context" - "io" - "log" - "log/slog" - - "github.com/hashicorp/go-hclog" -) - -type SLogHCAdapter struct { - ctx context.Context - logger *slog.Logger - level *slog.LevelVar -} - -func NewSLogHCAdapter(logger *slog.Logger) *SLogHCAdapter { - return &SLogHCAdapter{context.Background(), logger, new(slog.LevelVar)} -} - -func (l *SLogHCAdapter) IsTrace() bool { return false } - -func (l *SLogHCAdapter) IsDebug() bool { - return l.logger.Handler().Enabled(l.ctx, slog.LevelDebug) -} - -func (l *SLogHCAdapter) IsInfo() bool { - return l.logger.Handler().Enabled(l.ctx, slog.LevelInfo) -} - -func (l *SLogHCAdapter) IsWarn() bool { - return l.logger.Handler().Enabled(l.ctx, slog.LevelWarn) -} - -func (l *SLogHCAdapter) IsError() bool { - return l.logger.Handler().Enabled(l.ctx, slog.LevelError) -} - -func (l *SLogHCAdapter) Trace(format string, args ...interface{}) {} - -func (l *SLogHCAdapter) Debug(format string, args ...interface{}) { - l.logger.Debug(format) -} - -func (l *SLogHCAdapter) Info(format string, args ...interface{}) { - l.logger.Info(format) -} - -func (l *SLogHCAdapter) Warn(format string, args ...interface{}) { - l.logger.Warn(format) -} - -func (l *SLogHCAdapter) Error(format string, args ...interface{}) { - l.logger.Error(format) -} - -func (l *SLogHCAdapter) Log(level hclog.Level, format string, args ...interface{}) { - switch level { - case hclog.Debug: - l.Debug(format, args...) - case hclog.Info: - l.Info(format, args...) - case hclog.Warn: - l.Warn(format, args...) - case hclog.Error: - l.Error(format, args...) - } -} - -func (l *SLogHCAdapter) GetLevel() hclog.Level { - return sl2hcLogLevel(l.level.Level()) -} - -func (l *SLogHCAdapter) SetLevel(level hclog.Level) { - l.level.Set(hc2slLogLevel(level)) -} - -func (l *SLogHCAdapter) Name() string { - return "" -} - -func (l *SLogHCAdapter) Named(name string) hclog.Logger { - return &SLogHCAdapter{l.ctx, l.logger.With("name", name), l.level} -} - -func (l *SLogHCAdapter) ResetNamed(name string) hclog.Logger { - return &SLogHCAdapter{l.ctx, l.logger.With("name", nil), l.level} -} - -func (l *SLogHCAdapter) With(args ...any) hclog.Logger { - return &SLogHCAdapter{l.ctx, l.logger.With(), l.level} -} - -func (l *SLogHCAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { - return slog.NewLogLogger(l.logger.Handler(), slog.LevelInfo) -} - -func (l *SLogHCAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { - return slog.NewLogLogger(l.logger.Handler(), slog.LevelInfo).Writer() -} - -func (l *SLogHCAdapter) ImpliedArgs() []interface{} { - return nil -} - -func hc2slLogLevel(lvl hclog.Level) slog.Level { - switch lvl { - case hclog.Debug, hclog.Trace, hclog.NoLevel: - return slog.LevelDebug - case hclog.Info, hclog.DefaultLevel: - return slog.LevelInfo - case hclog.Warn: - return slog.LevelWarn - case hclog.Error, hclog.Off: - return slog.LevelError - default: - return slog.LevelInfo - } -} - -func sl2hcLogLevel(lvl slog.Level) hclog.Level { - switch lvl { - case slog.LevelDebug: - return hclog.Debug - case slog.LevelInfo: - return hclog.Info - case slog.LevelWarn: - return hclog.Warn - case slog.LevelError: - return hclog.Error - default: - return hclog.NoLevel - } -}