-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(server/v2): use only one logger for app and server (#22241)
(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
1 parent
ba5966f
commit 431b47d
Showing
7 changed files
with
712 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>')") | ||
rootCmd.PersistentFlags().String(FlagLogFormat, "plain", "The logging format (json|plain)") | ||
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], | ||
globalServerCfg 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], | ||
) *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 | ||
} |
Oops, something went wrong.