From e085d23a9526c99bef0e4823734f7b9f1a0e2764 Mon Sep 17 00:00:00 2001 From: Manuel Schmidt Date: Tue, 21 May 2024 16:29:12 +0200 Subject: [PATCH 1/5] feat: add cobra cli library --- cmd/bursa/api.go | 41 ++++++++----- cmd/bursa/main.go | 99 +++++++++++-------------------- cmd/bursa/{cli.go => new.go} | 33 ++++++++++- go.mod | 3 + go.sum | 8 +++ internal/api/api.go | 28 ++++----- internal/cli/cli.go | 37 +++++------- internal/consolelog/consolelog.go | 77 ++++++++++++++++++++++++ internal/logging/logging.go | 58 ------------------ 9 files changed, 204 insertions(+), 180 deletions(-) rename cmd/bursa/{cli.go => new.go} (53%) create mode 100644 internal/consolelog/consolelog.go delete mode 100644 internal/logging/logging.go diff --git a/cmd/bursa/api.go b/cmd/bursa/api.go index 141f498..4f8dca0 100644 --- a/cmd/bursa/api.go +++ b/cmd/bursa/api.go @@ -15,24 +15,35 @@ package main import ( + "fmt" + "log/slog" + "os" + "github.com/blinklabs-io/bursa/internal/api" "github.com/blinklabs-io/bursa/internal/config" - "github.com/blinklabs-io/bursa/internal/logging" + "github.com/spf13/cobra" ) -func apiMain() { - cfg := config.GetConfig() - logger := logging.GetLogger() - // Start API listener - logger.Infof( - "starting API listener on %s:%d", - cfg.Api.ListenAddress, - cfg.Api.ListenPort, - ) - if err := api.Start(cfg); err != nil { - logger.Fatalf("failed to start API: %s", err) - } +func apiCommand() *cobra.Command { + apiCommand := cobra.Command{ + Use: "api", + Short: "Runs the api", + Run: func(cmd *cobra.Command, args []string) { + cfg := config.GetConfig() + // Start API listener + slog.Info(fmt.Sprintf( + "starting API listener on %s:%d", + cfg.Api.ListenAddress, + cfg.Api.ListenPort, + )) + if err := api.Start(cfg); err != nil { + slog.Error("failed to start API: %s", err) + os.Exit(1) + } - // Wait forever - select {} + // Wait forever + select {} + }, + } + return &apiCommand } diff --git a/cmd/bursa/main.go b/cmd/bursa/main.go index d18ab8b..b02261e 100644 --- a/cmd/bursa/main.go +++ b/cmd/bursa/main.go @@ -15,80 +15,49 @@ package main import ( - "flag" - "fmt" + "log/slog" "os" - "github.com/blinklabs-io/bursa/internal/config" - "github.com/blinklabs-io/bursa/internal/logging" + "github.com/blinklabs-io/bursa/internal/consolelog" + "github.com/spf13/cobra" +) + +const ( + programName = "bursa" ) func main() { - var appName string - if os.Args == nil { - appName = "bursa" - } else { - appName = os.Args[0] - } - fs := flag.NewFlagSet(appName, flag.ExitOnError) - fs.Usage = func() { - fmt.Fprintf( - flag.CommandLine.Output(), - "Usage: %s [-h] [args]\n\nSubcommands:\n\n", - appName, - ) - fmt.Fprintf( - flag.CommandLine.Output(), - " - %-18s %s\n", - "api", - "run an API server", - ) - fmt.Fprintf( - flag.CommandLine.Output(), - " - %-18s %s\n", - "cli", - "run a terminal command", - ) - } - if os.Args == nil { - fs.Usage() - os.Exit(1) - } - _ = fs.Parse(os.Args[1:]) // ignore parse errors + globalFlags := struct { + debug bool + }{} - // Load Config - _, err := config.LoadConfig() - if err != nil { - fmt.Printf("Failed to load config: %s\n", err) - os.Exit(1) + rootCmd := &cobra.Command{ + Use: programName, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + // Configure default logger + logLevel := slog.LevelInfo + if globalFlags.debug { + logLevel = slog.LevelDebug + } + logger := slog.New( + consolelog.NewHandler(os.Stdout, &slog.HandlerOptions{ + Level: logLevel, + }), + ) + slog.SetDefault(logger) + }, } - // Configure logging - logging.Setup() - logger := logging.GetLogger() - // Sync logger on exit - defer func() { - if err := logger.Sync(); err != nil { - // ignore error - return - } - }() - var subCommand string - // Parse subcommand - if len(fs.Args()) < 1 { - fs.Usage() - os.Exit(1) - } else { - subCommand = fs.Arg(0) - } + // Global flags + rootCmd.PersistentFlags().BoolVarP(&globalFlags.debug, "debug", "D", false, "enable debug logging") + + rootCmd.AddCommand( + newCommand(), + apiCommand(), + ) - switch subCommand { - case "api": - apiMain() - case "cli": - cliMain() - default: - fmt.Printf("Unknown subcommand: %s\n", subCommand) + if err := rootCmd.Execute(); err != nil { + // NOTE: we purposely don't display the error, since cobra will have already displayed it os.Exit(1) } } diff --git a/cmd/bursa/cli.go b/cmd/bursa/new.go similarity index 53% rename from cmd/bursa/cli.go rename to cmd/bursa/new.go index 56a3e29..3486c60 100644 --- a/cmd/bursa/cli.go +++ b/cmd/bursa/new.go @@ -1,4 +1,4 @@ -// Copyright 2023 Blink Labs Software +// Copyright 2024 Blink Labs Software // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,8 +16,35 @@ package main import ( "github.com/blinklabs-io/bursa/internal/cli" + "github.com/spf13/cobra" ) -func cliMain() { - cli.Run() +var ( + output string +) + +func newCommand() *cobra.Command { + newCommand := &cobra.Command{ + Use: "new", + } + + newCommand.AddCommand( + newWalletCommand(), + ) + return newCommand +} + +func newWalletCommand() *cobra.Command { + newWalletCommand := cobra.Command{ + Use: "wallet", + Short: "Creates a new wallet", + Run: func(cmd *cobra.Command, args []string) { + cli.Run(output) + }, + } + + newWalletCommand.PersistentFlags().StringVar(&output, "output", "", "") + + return &newWalletCommand } + diff --git a/go.mod b/go.mod index d25dbe2..1ce03ff 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect @@ -55,6 +56,8 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/go.sum b/go.sum index 47fd68d..56a75f6 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,7 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -193,6 +194,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -283,9 +286,14 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/internal/api/api.go b/internal/api/api.go index 1de22b1..f788d7c 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -17,9 +17,9 @@ package api import ( "fmt" "net/http" - "time" - ginzap "github.com/gin-contrib/zap" + "log/slog" + "github.com/gin-gonic/gin" "github.com/penglongli/gin-metrics/ginmetrics" swaggerFiles "github.com/swaggo/files" // swagger embed files @@ -27,7 +27,6 @@ import ( "github.com/blinklabs-io/bursa" "github.com/blinklabs-io/bursa/internal/config" - "github.com/blinklabs-io/bursa/internal/logging" _ "github.com/blinklabs-io/bursa/docs" // docs is generated by Swag CLI ) @@ -58,16 +57,14 @@ func Start(cfg *config.Config) error { router := gin.New() // Catch panics and return a 500 router.Use(gin.Recovery()) - // Standard logging - logger := logging.GetLogger() // Access logging - accessLogger := logging.GetAccessLogger() - router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{ - TimeFormat: time.RFC3339, - UTC: true, - SkipPaths: []string{}, - })) - router.Use(ginzap.RecoveryWithZap(accessLogger, true)) + // accessLogger := logging.GetAccessLogger() + //router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{ + // TimeFormat: time.RFC3339, + // UTC: true, + // SkipPaths: []string{}, + //})) + //router.Use(ginzap.RecoveryWithZap(accessLogger, true)) // Create a healthcheck router.GET("/healthcheck", handleHealthcheck) @@ -111,7 +108,7 @@ func Start(cfg *config.Config) error { // Start metrics listener go func() { // TODO: return error if we cannot initialize metrics - logger.Infof("starting metrics listener on %s:%d", + slog.Info("starting metrics listener on %s:%d", cfg.Metrics.ListenAddress, cfg.Metrics.ListenPort, ) @@ -145,11 +142,10 @@ func handleHealthcheck(c *gin.Context) { // @Success 200 {object} bursa.Wallet "Ok" // @Router /api/wallet/create [get] func handleWalletCreate(c *gin.Context) { - logger := logging.GetLogger() mnemonic, err := bursa.NewMnemonic() if err != nil { - logger.Errorf("failed to load mnemonic: %s", err) + slog.Error("failed to load mnemonic: %s", err) c.JSON(500, fmt.Sprintf("failed to load mnemonic: %s", err)) _ = ginmetrics.GetMonitor(). GetMetric("bursa_wallets_fail_count"). @@ -159,7 +155,7 @@ func handleWalletCreate(c *gin.Context) { w, err := bursa.NewDefaultWallet(mnemonic) if err != nil { - logger.Errorf("failed to initialize wallet: %s", err) + slog.Error("failed to initialize wallet: %s", err) c.JSON(500, fmt.Sprintf("failed to initialize wallet: %s", err)) _ = ginmetrics.GetMonitor(). GetMetric("bursa_wallets_fail_count"). diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 698c02e..36c78df 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -15,8 +15,8 @@ package cli import ( - "flag" "fmt" + "log/slog" "os" "path/filepath" @@ -24,39 +24,30 @@ import ( "github.com/blinklabs-io/bursa" "github.com/blinklabs-io/bursa/internal/config" - "github.com/blinklabs-io/bursa/internal/logging" ) -func Run() { - fs := flag.NewFlagSet("cli", flag.ExitOnError) - flagOutput := fs.String( - "output", - "", - "output directory for files, otherwise uses STDOUT", - ) - if len(os.Args) >= 2 { - _ = fs.Parse(os.Args[2:]) // ignore parse errors - } +func Run(output string) { cfg := config.GetConfig() - logger := logging.GetLogger() // Load mnemonic var err error mnemonic := cfg.Mnemonic if mnemonic == "" { mnemonic, err = bursa.NewMnemonic() if err != nil { - logger.Fatalf("failed to load mnemonic: %s", err) + slog.Error("failed to load mnemonic: %s", err) + return } } w, err := bursa.NewDefaultWallet(mnemonic) if err != nil { - logger.Fatalf("failed to initialize wallet: %s", err) + slog.Error("failed to initialize wallet: %s", err) + return } - logger.Infof("Loaded mnemonic and generated address...") + slog.Info("Loaded mnemonic and generated address...") - if *flagOutput == "" { + if output == "" { fmt.Printf("MNEMONIC=%s\n", w.Mnemonic) fmt.Printf("PAYMENT_ADDRESS=%s\n", w.PaymentAddress) fmt.Printf("STAKE_ADDRESS=%s\n", w.StakeAddress) @@ -68,10 +59,10 @@ func Run() { fmt.Printf("stake.skey=%s\n", bursa.GetKeyFile(w.StakeSKey)) fmt.Printf("stakeExtended.skey=%s\n", bursa.GetKeyFile(w.StakeExtendedSKey)) } else { - fmt.Printf("Output dir: %v\n", *flagOutput) - _, err := os.Stat(*flagOutput) + fmt.Printf("Output dir: %v\n", output) + _, err := os.Stat(output) if os.IsNotExist(err) { - err = os.MkdirAll(*flagOutput, 0755) + err = os.MkdirAll(output, 0755) if err != nil { panic(err) } @@ -93,7 +84,7 @@ func Run() { k := k v := v g.Go(func() error { - path := filepath.Join(*flagOutput, k) + path := filepath.Join(output, k) err = os.WriteFile(path, []byte(v), 0666) if err != nil { return err @@ -104,10 +95,10 @@ func Run() { } err = g.Wait() if err != nil { - logger.Fatalf("error occurred: %s", err) + slog.Error("error occurred: %s", err) os.Exit(1) } - logger.Infof("wrote output files to %s", *flagOutput) + slog.Info(fmt.Sprintf("wrote output files to %s", output)) } } diff --git a/internal/consolelog/consolelog.go b/internal/consolelog/consolelog.go new file mode 100644 index 0000000..6f7b9f0 --- /dev/null +++ b/internal/consolelog/consolelog.go @@ -0,0 +1,77 @@ +// Copyright 2024 Blink Labs Software +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package consolelog + +import ( + "context" + "fmt" + "io" + "log/slog" +) + +const ( + colorBrightRed = "91" + colorBrightYellow = "93" + colorBrightMagenta = "95" +) + +type Handler struct { + h slog.Handler + out io.Writer +} + +func NewHandler(out io.Writer, opts *slog.HandlerOptions) *Handler { + if opts == nil { + opts = &slog.HandlerOptions{} + } + return &Handler{ + out: out, + h: slog.NewTextHandler(out, &slog.HandlerOptions{ + Level: opts.Level, + }), + } +} + +func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool { + return h.h.Enabled(ctx, level) +} + +func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &Handler{h: h.h.WithAttrs(attrs)} +} + +func (h *Handler) WithGroup(name string) slog.Handler { + return &Handler{h: h.h.WithGroup(name)} +} + +func (h *Handler) Handle(ctx context.Context, r slog.Record) error { + var levelTag string + switch r.Level { + case slog.LevelDebug: + levelTag = fmt.Sprintf("\033[%smDEBUG:\033[0m ", colorBrightMagenta) + case slog.LevelInfo: + // No tag for INFO + levelTag = "" + case slog.LevelWarn: + levelTag = fmt.Sprintf("\033[%smWARNING:\033[0m ", colorBrightYellow) + case slog.LevelError: + levelTag = fmt.Sprintf("\033[%smERROR:\033[0m ", colorBrightRed) + } + msg := levelTag + r.Message + "\n" + if _, err := h.out.Write([]byte(msg)); err != nil { + return err + } + return nil +} diff --git a/internal/logging/logging.go b/internal/logging/logging.go deleted file mode 100644 index 149addd..0000000 --- a/internal/logging/logging.go +++ /dev/null @@ -1,58 +0,0 @@ -package logging - -import ( - "log" - "time" - - "github.com/blinklabs-io/bursa/internal/config" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type Logger = zap.SugaredLogger - -var globalLogger *Logger - -func Setup() { - cfg := config.GetConfig() - // Build our custom logging config - loggerConfig := zap.NewProductionConfig() - // Change timestamp key name - loggerConfig.EncoderConfig.TimeKey = "timestamp" - // Use a human readable time format - loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout( - time.RFC3339, - ) - - // Set level - if cfg.Logging.Level != "" { - level, err := zapcore.ParseLevel(cfg.Logging.Level) - if err != nil { - log.Fatalf("error configuring logger: %s", err) - } - loggerConfig.Level.SetLevel(level) - } - - // Create the logger - l, err := loggerConfig.Build() - if err != nil { - log.Fatal(err) - } - - // Store the "sugared" version of the logger - globalLogger = l.Sugar() -} - -func GetLogger() *Logger { - return globalLogger -} - -func GetDesugaredLogger() *zap.Logger { - return globalLogger.Desugar() -} - -func GetAccessLogger() *zap.Logger { - return globalLogger.Desugar(). - With(zap.String("type", "access")). - WithOptions(zap.WithCaller(false)) -} From d7b75584eb765aba9c7bd476594b8df01227c60f Mon Sep 17 00:00:00 2001 From: Manuel Schmidt Date: Wed, 22 May 2024 15:45:59 +0200 Subject: [PATCH 2/5] fix: forgot to add go.mod and go.sum --- go.mod | 5 +---- go.sum | 7 ------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 1ce03ff..3f942d0 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,14 @@ go 1.20 require ( github.com/fivebinaries/go-cardano-serialization v0.0.0-20220907134105-ec9b85086588 github.com/fxamacker/cbor/v2 v2.6.0 - github.com/gin-contrib/zap v1.1.3 github.com/gin-gonic/gin v1.10.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/penglongli/gin-metrics v0.1.10 + github.com/spf13/cobra v1.8.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.3 github.com/tyler-smith/go-bip39 v1.1.0 - go.uber.org/zap v1.27.0 golang.org/x/sync v0.7.0 ) @@ -56,12 +55,10 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/x448/float16 v0.8.4 // indirect - go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect diff --git a/go.sum b/go.sum index 56a75f6..7da8768 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,6 @@ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcP github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-contrib/zap v1.1.3 h1:9e/U9fYd4/OBfmSEBs5hHZq114uACn7bpuzvCkcJySA= -github.com/gin-contrib/zap v1.1.3/go.mod h1:+BD/6NYZKJyUpqVoJEvgeq9GLz8pINEQvak9LHNOTSE= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= @@ -335,11 +333,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -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/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= From 5f3fa545e64ef32ecbe1eeda48155ec6c1a3baa0 Mon Sep 17 00:00:00 2001 From: Manuel Schmidt Date: Wed, 22 May 2024 15:56:29 +0200 Subject: [PATCH 3/5] fix: hide shell completion command --- cmd/bursa/main.go | 3 +++ cmd/bursa/new.go | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/bursa/main.go b/cmd/bursa/main.go index b02261e..458cff2 100644 --- a/cmd/bursa/main.go +++ b/cmd/bursa/main.go @@ -33,6 +33,9 @@ func main() { rootCmd := &cobra.Command{ Use: programName, + CompletionOptions: cobra.CompletionOptions{ + DisableDefaultCmd: true, + }, PersistentPreRun: func(cmd *cobra.Command, args []string) { // Configure default logger logLevel := slog.LevelInfo diff --git a/cmd/bursa/new.go b/cmd/bursa/new.go index 3486c60..4f64d00 100644 --- a/cmd/bursa/new.go +++ b/cmd/bursa/new.go @@ -20,12 +20,12 @@ import ( ) var ( - output string + output string ) func newCommand() *cobra.Command { newCommand := &cobra.Command{ - Use: "new", + Use: "new", } newCommand.AddCommand( @@ -47,4 +47,3 @@ func newWalletCommand() *cobra.Command { return &newWalletCommand } - From 197912e2891769434058c39e1d943199a07cdc0d Mon Sep 17 00:00:00 2001 From: Manuel Schmidt Date: Wed, 22 May 2024 20:40:22 +0200 Subject: [PATCH 4/5] fix: restore package internal/logging --- cmd/bursa/api.go | 14 +++--- cmd/bursa/main.go | 32 ++++--------- go.mod | 3 ++ go.sum | 7 +++ internal/api/api.go | 28 ++++++----- internal/cli/cli.go | 16 +++---- internal/consolelog/consolelog.go | 77 ------------------------------- internal/logging/logging.go | 58 +++++++++++++++++++++++ 8 files changed, 107 insertions(+), 128 deletions(-) delete mode 100644 internal/consolelog/consolelog.go create mode 100644 internal/logging/logging.go diff --git a/cmd/bursa/api.go b/cmd/bursa/api.go index 4f8dca0..a032bf8 100644 --- a/cmd/bursa/api.go +++ b/cmd/bursa/api.go @@ -15,12 +15,9 @@ package main import ( - "fmt" - "log/slog" - "os" - "github.com/blinklabs-io/bursa/internal/api" "github.com/blinklabs-io/bursa/internal/config" + "github.com/blinklabs-io/bursa/internal/logging" "github.com/spf13/cobra" ) @@ -31,14 +28,15 @@ func apiCommand() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cfg := config.GetConfig() // Start API listener - slog.Info(fmt.Sprintf( + logger := logging.GetLogger() + // Start API listener + logger.Infof( "starting API listener on %s:%d", cfg.Api.ListenAddress, cfg.Api.ListenPort, - )) + ) if err := api.Start(cfg); err != nil { - slog.Error("failed to start API: %s", err) - os.Exit(1) + logger.Fatalf("failed to start API: %s", err) } // Wait forever diff --git a/cmd/bursa/main.go b/cmd/bursa/main.go index 458cff2..758c6db 100644 --- a/cmd/bursa/main.go +++ b/cmd/bursa/main.go @@ -15,10 +15,9 @@ package main import ( - "log/slog" "os" - "github.com/blinklabs-io/bursa/internal/consolelog" + "github.com/blinklabs-io/bursa/internal/logging" "github.com/spf13/cobra" ) @@ -27,40 +26,29 @@ const ( ) func main() { - globalFlags := struct { - debug bool - }{} + // Configure logging + logging.Setup() + logger := logging.GetLogger() + defer func() { + if err := logger.Sync(); err != nil { + // ignore error + return + } + }() rootCmd := &cobra.Command{ Use: programName, CompletionOptions: cobra.CompletionOptions{ DisableDefaultCmd: true, }, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - // Configure default logger - logLevel := slog.LevelInfo - if globalFlags.debug { - logLevel = slog.LevelDebug - } - logger := slog.New( - consolelog.NewHandler(os.Stdout, &slog.HandlerOptions{ - Level: logLevel, - }), - ) - slog.SetDefault(logger) - }, } - // Global flags - rootCmd.PersistentFlags().BoolVarP(&globalFlags.debug, "debug", "D", false, "enable debug logging") - rootCmd.AddCommand( newCommand(), apiCommand(), ) if err := rootCmd.Execute(); err != nil { - // NOTE: we purposely don't display the error, since cobra will have already displayed it os.Exit(1) } } diff --git a/go.mod b/go.mod index 3f942d0..2c815b4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/fivebinaries/go-cardano-serialization v0.0.0-20220907134105-ec9b85086588 github.com/fxamacker/cbor/v2 v2.6.0 + github.com/gin-contrib/zap v1.1.3 github.com/gin-gonic/gin v1.10.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/penglongli/gin-metrics v0.1.10 @@ -13,6 +14,7 @@ require ( github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.3 github.com/tyler-smith/go-bip39 v1.1.0 + go.uber.org/zap v1.27.0 golang.org/x/sync v0.7.0 ) @@ -59,6 +61,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/x448/float16 v0.8.4 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect diff --git a/go.sum b/go.sum index 7da8768..56a75f6 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,8 @@ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcP github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/zap v1.1.3 h1:9e/U9fYd4/OBfmSEBs5hHZq114uACn7bpuzvCkcJySA= +github.com/gin-contrib/zap v1.1.3/go.mod h1:+BD/6NYZKJyUpqVoJEvgeq9GLz8pINEQvak9LHNOTSE= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= @@ -333,6 +335,11 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +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/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= diff --git a/internal/api/api.go b/internal/api/api.go index f788d7c..8e4ed4c 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -17,8 +17,9 @@ package api import ( "fmt" "net/http" + "time" - "log/slog" + ginzap "github.com/gin-contrib/zap" "github.com/gin-gonic/gin" "github.com/penglongli/gin-metrics/ginmetrics" @@ -27,6 +28,7 @@ import ( "github.com/blinklabs-io/bursa" "github.com/blinklabs-io/bursa/internal/config" + "github.com/blinklabs-io/bursa/internal/logging" _ "github.com/blinklabs-io/bursa/docs" // docs is generated by Swag CLI ) @@ -52,19 +54,20 @@ func Start(cfg *config.Config) error { // Disable gin debug and color output gin.SetMode(gin.ReleaseMode) gin.DisableConsoleColor() - // Configure API router router := gin.New() // Catch panics and return a 500 router.Use(gin.Recovery()) + // Standard logging + logger := logging.GetLogger() // Access logging - // accessLogger := logging.GetAccessLogger() - //router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{ - // TimeFormat: time.RFC3339, - // UTC: true, - // SkipPaths: []string{}, - //})) - //router.Use(ginzap.RecoveryWithZap(accessLogger, true)) + accessLogger := logging.GetAccessLogger() + router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{ + TimeFormat: time.RFC3339, + UTC: true, + SkipPaths: []string{}, + })) + router.Use(ginzap.RecoveryWithZap(accessLogger, true)) // Create a healthcheck router.GET("/healthcheck", handleHealthcheck) @@ -108,7 +111,7 @@ func Start(cfg *config.Config) error { // Start metrics listener go func() { // TODO: return error if we cannot initialize metrics - slog.Info("starting metrics listener on %s:%d", + logger.Infof("starting metrics listener on %s:%d", cfg.Metrics.ListenAddress, cfg.Metrics.ListenPort, ) @@ -142,10 +145,11 @@ func handleHealthcheck(c *gin.Context) { // @Success 200 {object} bursa.Wallet "Ok" // @Router /api/wallet/create [get] func handleWalletCreate(c *gin.Context) { + logger := logging.GetLogger() mnemonic, err := bursa.NewMnemonic() if err != nil { - slog.Error("failed to load mnemonic: %s", err) + logger.Errorf("failed to load mnemonic: %s", err) c.JSON(500, fmt.Sprintf("failed to load mnemonic: %s", err)) _ = ginmetrics.GetMonitor(). GetMetric("bursa_wallets_fail_count"). @@ -155,7 +159,7 @@ func handleWalletCreate(c *gin.Context) { w, err := bursa.NewDefaultWallet(mnemonic) if err != nil { - slog.Error("failed to initialize wallet: %s", err) + logger.Errorf("failed to initialize wallet: %s", err) c.JSON(500, fmt.Sprintf("failed to initialize wallet: %s", err)) _ = ginmetrics.GetMonitor(). GetMetric("bursa_wallets_fail_count"). diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 36c78df..d149d76 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -16,7 +16,6 @@ package cli import ( "fmt" - "log/slog" "os" "path/filepath" @@ -24,28 +23,27 @@ import ( "github.com/blinklabs-io/bursa" "github.com/blinklabs-io/bursa/internal/config" + "github.com/blinklabs-io/bursa/internal/logging" ) func Run(output string) { - cfg := config.GetConfig() + logger := logging.GetLogger() // Load mnemonic var err error mnemonic := cfg.Mnemonic if mnemonic == "" { mnemonic, err = bursa.NewMnemonic() if err != nil { - slog.Error("failed to load mnemonic: %s", err) - return + logger.Fatalf("failed to load mnemonic: %s", err) } } w, err := bursa.NewDefaultWallet(mnemonic) if err != nil { - slog.Error("failed to initialize wallet: %s", err) - return + logger.Fatalf("failed to initialize wallet: %s", err) } - slog.Info("Loaded mnemonic and generated address...") + logger.Info("Loaded mnemonic and generated address...") if output == "" { fmt.Printf("MNEMONIC=%s\n", w.Mnemonic) @@ -95,10 +93,10 @@ func Run(output string) { } err = g.Wait() if err != nil { - slog.Error("error occurred: %s", err) + logger.Fatalf("error occurred: %s", err) os.Exit(1) } - slog.Info(fmt.Sprintf("wrote output files to %s", output)) + logger.Infof("wrote output files to %s", output) } } diff --git a/internal/consolelog/consolelog.go b/internal/consolelog/consolelog.go deleted file mode 100644 index 6f7b9f0..0000000 --- a/internal/consolelog/consolelog.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024 Blink Labs Software -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package consolelog - -import ( - "context" - "fmt" - "io" - "log/slog" -) - -const ( - colorBrightRed = "91" - colorBrightYellow = "93" - colorBrightMagenta = "95" -) - -type Handler struct { - h slog.Handler - out io.Writer -} - -func NewHandler(out io.Writer, opts *slog.HandlerOptions) *Handler { - if opts == nil { - opts = &slog.HandlerOptions{} - } - return &Handler{ - out: out, - h: slog.NewTextHandler(out, &slog.HandlerOptions{ - Level: opts.Level, - }), - } -} - -func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool { - return h.h.Enabled(ctx, level) -} - -func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler { - return &Handler{h: h.h.WithAttrs(attrs)} -} - -func (h *Handler) WithGroup(name string) slog.Handler { - return &Handler{h: h.h.WithGroup(name)} -} - -func (h *Handler) Handle(ctx context.Context, r slog.Record) error { - var levelTag string - switch r.Level { - case slog.LevelDebug: - levelTag = fmt.Sprintf("\033[%smDEBUG:\033[0m ", colorBrightMagenta) - case slog.LevelInfo: - // No tag for INFO - levelTag = "" - case slog.LevelWarn: - levelTag = fmt.Sprintf("\033[%smWARNING:\033[0m ", colorBrightYellow) - case slog.LevelError: - levelTag = fmt.Sprintf("\033[%smERROR:\033[0m ", colorBrightRed) - } - msg := levelTag + r.Message + "\n" - if _, err := h.out.Write([]byte(msg)); err != nil { - return err - } - return nil -} diff --git a/internal/logging/logging.go b/internal/logging/logging.go new file mode 100644 index 0000000..149addd --- /dev/null +++ b/internal/logging/logging.go @@ -0,0 +1,58 @@ +package logging + +import ( + "log" + "time" + + "github.com/blinklabs-io/bursa/internal/config" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type Logger = zap.SugaredLogger + +var globalLogger *Logger + +func Setup() { + cfg := config.GetConfig() + // Build our custom logging config + loggerConfig := zap.NewProductionConfig() + // Change timestamp key name + loggerConfig.EncoderConfig.TimeKey = "timestamp" + // Use a human readable time format + loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout( + time.RFC3339, + ) + + // Set level + if cfg.Logging.Level != "" { + level, err := zapcore.ParseLevel(cfg.Logging.Level) + if err != nil { + log.Fatalf("error configuring logger: %s", err) + } + loggerConfig.Level.SetLevel(level) + } + + // Create the logger + l, err := loggerConfig.Build() + if err != nil { + log.Fatal(err) + } + + // Store the "sugared" version of the logger + globalLogger = l.Sugar() +} + +func GetLogger() *Logger { + return globalLogger +} + +func GetDesugaredLogger() *zap.Logger { + return globalLogger.Desugar() +} + +func GetAccessLogger() *zap.Logger { + return globalLogger.Desugar(). + With(zap.String("type", "access")). + WithOptions(zap.WithCaller(false)) +} From adcc1d94c06776230a7eaf532ddae7bdf4eac788 Mon Sep 17 00:00:00 2001 From: Manuel Schmidt Date: Wed, 22 May 2024 20:56:06 +0200 Subject: [PATCH 5/5] fix: renamed command new to wallet --- cmd/bursa/main.go | 2 +- cmd/bursa/{new.go => wallet.go} | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) rename cmd/bursa/{new.go => wallet.go} (69%) diff --git a/cmd/bursa/main.go b/cmd/bursa/main.go index 758c6db..03fc8f6 100644 --- a/cmd/bursa/main.go +++ b/cmd/bursa/main.go @@ -44,7 +44,7 @@ func main() { } rootCmd.AddCommand( - newCommand(), + walletCommand(), apiCommand(), ) diff --git a/cmd/bursa/new.go b/cmd/bursa/wallet.go similarity index 69% rename from cmd/bursa/new.go rename to cmd/bursa/wallet.go index 4f64d00..a9328f2 100644 --- a/cmd/bursa/new.go +++ b/cmd/bursa/wallet.go @@ -23,27 +23,28 @@ var ( output string ) -func newCommand() *cobra.Command { - newCommand := &cobra.Command{ - Use: "new", +func walletCommand() *cobra.Command { + walletCommand := cobra.Command{ + Use: "wallet", + Short: "Wallet commands", } - newCommand.AddCommand( - newWalletCommand(), + walletCommand.AddCommand( + walletCreateCommand(), ) - return newCommand + return &walletCommand } -func newWalletCommand() *cobra.Command { - newWalletCommand := cobra.Command{ - Use: "wallet", +func walletCreateCommand() *cobra.Command { + walletCreateCommand := cobra.Command{ + Use: "create", Short: "Creates a new wallet", Run: func(cmd *cobra.Command, args []string) { cli.Run(output) }, } - newWalletCommand.PersistentFlags().StringVar(&output, "output", "", "") + walletCreateCommand.PersistentFlags().StringVar(&output, "output", "", "") - return &newWalletCommand + return &walletCreateCommand }