Skip to content

Commit

Permalink
fix(server/v2): use only one logger for app and server (#22241)
Browse files Browse the repository at this point in the history
(cherry picked from commit 1f941bb)

# Conflicts:
#	server/v2/commands.go
#	server/v2/server.go
#	server/v2/server_test.go
#	server/v2/util.go
  • Loading branch information
julienrbrt authored and mergify[bot] committed Oct 14, 2024
1 parent ba5966f commit 431b47d
Showing 7 changed files with 712 additions and 10 deletions.
211 changes: 211 additions & 0 deletions server/v2/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package serverv2

import (
"context"
"errors"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"

"github.com/spf13/cobra"
"github.com/spf13/viper"

"cosmossdk.io/core/transaction"
)

// Execute executes the root command of an application.
// It handles adding core CLI flags, specifically the logging flags.
func Execute(rootCmd *cobra.Command, envPrefix, defaultHome string) error {
rootCmd.PersistentFlags().String(FlagLogLevel, "info", "The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>')")

Check failure on line 21 in server/v2/commands.go

GitHub Actions / dependency-review

undefined: FlagLogLevel
rootCmd.PersistentFlags().String(FlagLogFormat, "plain", "The logging format (json|plain)")

Check failure on line 22 in server/v2/commands.go

GitHub Actions / dependency-review

undefined: FlagLogFormat
rootCmd.PersistentFlags().Bool(FlagLogNoColor, false, "Disable colored logs")
rootCmd.PersistentFlags().StringP(FlagHome, "", defaultHome, "directory for config and data")

// update the global viper with the root command's configuration
viper.SetEnvPrefix(envPrefix)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
viper.AutomaticEnv()

return rootCmd.Execute()
}

// AddCommands add the server commands to the root command
// It configures the config handling and the logger handling
func AddCommands[T transaction.Tx](
rootCmd *cobra.Command,
newApp AppCreator[T],

Check failure on line 38 in server/v2/commands.go

GitHub Actions / dependency-review

undefined: AppCreator
globalServerCfg ServerConfig,

Check failure on line 39 in server/v2/commands.go

GitHub Actions / dependency-review

undefined: ServerConfig
components ...ServerComponent[T],
) error {
if len(components) == 0 {
return errors.New("no components provided")
}

server := NewServer(globalServerCfg, components...)
originalPersistentPreRunE := rootCmd.PersistentPreRunE
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
// set the default command outputs
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())

if err := configHandle(server, cmd); err != nil {
return err
}

// call the original PersistentPreRun(E) if it exists
if rootCmd.PersistentPreRun != nil {
rootCmd.PersistentPreRun(cmd, args)
return nil
}

return originalPersistentPreRunE(cmd, args)
}

cmds := server.CLICommands()
startCmd := createStartCommand(server, newApp)
startCmd.SetContext(rootCmd.Context())
cmds.Commands = append(cmds.Commands, startCmd)
rootCmd.AddCommand(cmds.Commands...)

if len(cmds.Queries) > 0 {
if queryCmd := findSubCommand(rootCmd, "query"); queryCmd != nil {
queryCmd.AddCommand(cmds.Queries...)
} else {
queryCmd := topLevelCmd(rootCmd.Context(), "query", "Querying subcommands")
queryCmd.Aliases = []string{"q"}
queryCmd.AddCommand(cmds.Queries...)
rootCmd.AddCommand(queryCmd)
}
}

if len(cmds.Txs) > 0 {
if txCmd := findSubCommand(rootCmd, "tx"); txCmd != nil {
txCmd.AddCommand(cmds.Txs...)
} else {
txCmd := topLevelCmd(rootCmd.Context(), "tx", "Transactions subcommands")
txCmd.AddCommand(cmds.Txs...)
rootCmd.AddCommand(txCmd)
}
}

return nil
}

// createStartCommand creates the start command for the application.
func createStartCommand[T transaction.Tx](
server *Server[T],
newApp AppCreator[T],

Check failure on line 99 in server/v2/commands.go

GitHub Actions / dependency-review

undefined: AppCreator
) *cobra.Command {
flags := server.StartFlags()

cmd := &cobra.Command{
Use: "start",
Short: "Run the application",
RunE: func(cmd *cobra.Command, args []string) error {
v := GetViperFromCmd(cmd)
l := GetLoggerFromCmd(cmd)
if err := v.BindPFlags(cmd.Flags()); err != nil {
return err
}

if err := server.Init(newApp(l, v), v.AllSettings(), l); err != nil {
return err
}

ctx, cancelFn := context.WithCancel(cmd.Context())
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
sig := <-sigCh
cancelFn()
cmd.Printf("caught %s signal\n", sig.String())

if err := server.Stop(ctx); err != nil {
cmd.PrintErrln("failed to stop servers:", err)
}
}()

if err := server.Start(ctx); err != nil {
return err
}

return nil
},
}

// add the start flags to the command
for _, startFlags := range flags {
cmd.Flags().AddFlagSet(startFlags)
}

return cmd
}

// configHandle writes the default config to the home directory if it does not exist and sets the server context
func configHandle[T transaction.Tx](s *Server[T], cmd *cobra.Command) error {
home, err := cmd.Flags().GetString(FlagHome)
if err != nil {
return err
}

configDir := filepath.Join(home, "config")

// we need to check app.toml as the config folder can already exist for the client.toml
if _, err := os.Stat(filepath.Join(configDir, "app.toml")); os.IsNotExist(err) {
if err = s.WriteConfig(configDir); err != nil {
return err
}
}

v, err := ReadConfig(configDir)
if err != nil {
return err
}

if err := v.BindPFlags(cmd.Flags()); err != nil {
return err
}

logger, err := NewLogger(v, cmd.OutOrStdout())
if err != nil {
return err
}

return SetCmdServerContext(cmd, v, logger)
}

// findSubCommand finds a sub-command of the provided command whose Use
// string is or begins with the provided subCmdName.
// It verifies the command's aliases as well.
func findSubCommand(cmd *cobra.Command, subCmdName string) *cobra.Command {
for _, subCmd := range cmd.Commands() {
use := subCmd.Use
if use == subCmdName || strings.HasPrefix(use, subCmdName+" ") {
return subCmd
}

for _, alias := range subCmd.Aliases {
if alias == subCmdName || strings.HasPrefix(alias, subCmdName+" ") {
return subCmd
}
}
}
return nil
}

// topLevelCmd creates a new top-level command with the provided name and
// description. The command will have DisableFlagParsing set to false and
// SuggestionsMinimumDistance set to 2.
func topLevelCmd(ctx context.Context, use, short string) *cobra.Command {
cmd := &cobra.Command{
Use: use,
Short: short,
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
}
cmd.SetContext(ctx)

return cmd
}
Loading

0 comments on commit 431b47d

Please sign in to comment.