From 7f2dca8b88fe396eb794b9904b97ed6e9010a637 Mon Sep 17 00:00:00 2001 From: lai Date: Thu, 8 Jul 2021 09:33:04 +0800 Subject: [PATCH] add option enable-default-config, add log --- cmd/root.go | 12 ++- generate/cli.go | 214 ++++++++++++++++++++++++++++++++++++++++++++---- readme.md | 6 +- 3 files changed, 212 insertions(+), 20 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index c2bc6a9..ad3e0a7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,8 +9,10 @@ import ( ) var ( - initWithOutConfig bool - rootCmd = &cobra.Command{ + initWithOutConfig bool + enableDefaultConfig bool + initWithOutLog bool + rootCmd = &cobra.Command{ Use: "readygo module_name out_dir", Short: "create empty project with cobra and spf13", Args: func(cmd *cobra.Command, args []string) error { @@ -24,7 +26,7 @@ var ( if len(args) > 1 { outdir = args[1] } - err := generate.InitCli(args[0], outdir, !initWithOutConfig) + err := generate.InitCli(args[0], outdir, initWithOutConfig, enableDefaultConfig, initWithOutLog) if err != nil { fmt.Printf("error: %s\n", err.Error()) } @@ -40,5 +42,7 @@ func Execute() { } func init() { - rootCmd.PersistentFlags().BoolVar(&initWithOutConfig, "without-config", false, "Not use configuration files") + rootCmd.PersistentFlags().BoolVar(&initWithOutConfig, "with-out-config", false, "Not use configuration files") + rootCmd.PersistentFlags().BoolVar(&initWithOutLog, "with-out-log", false, "Not generate logger") + rootCmd.PersistentFlags().BoolVar(&enableDefaultConfig, "enable-default-config", false, "generate default config in XDG_CONFIG_HOME, if config file not exist") } diff --git a/generate/cli.go b/generate/cli.go index 1480859..eb48145 100644 --- a/generate/cli.go +++ b/generate/cli.go @@ -15,6 +15,7 @@ var ( tmplNameMain = "main" tmplNameRoot = "root" tmplNameConfig = "config" + tmplNameLog = "log" ) func init() { @@ -23,6 +24,7 @@ func init() { tmplNameMain: tmplTextMain, tmplNameRoot: tmplTextRoot, tmplNameConfig: tmplTextConfig, + tmplNameLog: tmplTextLog, } for name, text := range tmpls { template.Must(tmpl.New(name).Parse(text)) @@ -30,12 +32,17 @@ func init() { } type tmplVarRoot struct { - Module string - WithConfig bool + Module string + WithConfig bool + EnableDefaultConfig bool } type tmplVarConfig struct { - Module string + Module string + EnableDefaultConfig bool +} + +type tmplVarLog struct { } type tmplVarMain struct { @@ -68,29 +75,37 @@ func getProjectDir(module, outdir string) (string, error) { return projectDir, nil } -func InitCli(module, outdir string, initWithConfig bool) error { +func InitCli(module, outdir string, initWithOutConfig, enableDefaultConfig, initWithOutLog bool) error { projectDir, err := getProjectDir(module, outdir) if err != nil { return err } var ( - cmdDir = fmt.Sprintf("%s/cmd", projectDir) - configDir = fmt.Sprintf("%s/config", projectDir) - filePathMain = fmt.Sprintf("%s/main.go", projectDir) - filePathRoot = fmt.Sprintf("%s/root.go", cmdDir) - filePathCocnfig = fmt.Sprintf("%s/config.go", configDir) + cmdDir = fmt.Sprintf("%s/cmd", projectDir) + configDir = fmt.Sprintf("%s/config", projectDir) + logDir = fmt.Sprintf("%s/logger", projectDir) + filePathMain = fmt.Sprintf("%s/main.go", projectDir) + filePathRoot = fmt.Sprintf("%s/root.go", cmdDir) + filePathConfig = fmt.Sprintf("%s/config.go", configDir) + filePathLog = fmt.Sprintf("%s/logger.go", logDir) ) err = mkdirAll(cmdDir) if err != nil { return err } - if initWithConfig { + if !initWithOutConfig { err = mkdirAll(configDir) if err != nil { return err } } + if !initWithOutLog { + err = mkdirAll(logDir) + if err != nil { + return err + } + } tocreate := map[string]string{ tmplNameMain: filePathMain, @@ -98,11 +113,15 @@ func InitCli(module, outdir string, initWithConfig bool) error { } tocreateData := map[string]interface{}{ tmplNameMain: tmplVarMain{module}, - tmplNameRoot: tmplVarRoot{module, initWithConfig}, + tmplNameRoot: tmplVarRoot{module, !initWithOutConfig, enableDefaultConfig}, } - if initWithConfig { - tocreate[tmplNameConfig] = filePathCocnfig - tocreateData[tmplNameConfig] = tmplVarConfig{module} + if !initWithOutConfig { + tocreate[tmplNameConfig] = filePathConfig + tocreateData[tmplNameConfig] = tmplVarConfig{module, enableDefaultConfig} + } + if !initWithOutLog { + tocreate[tmplNameLog] = filePathLog + tocreateData[tmplNameLog] = tmplVarLog{} } for tmplname, filepath := range tocreate { f, err := os.Create(filepath) @@ -175,8 +194,10 @@ import ( {{- .Module }}/config" {{- end }} {{- if .WithConfig }} + {{- if .EnableDefaultConfig }} "github.com/spf13/afero" {{- end }} + {{- end }} "github.com/spf13/cobra" {{- if .WithConfig }} "github.com/spf13/viper" @@ -217,14 +238,20 @@ func Execute() { {{- if .WithConfig }} func init() { cobra.OnInitialize(initConfig) + {{- if .EnableDefaultConfig }} rootCmd.PersistentFlags().StringVar(&configPath, "config", "", fmt.Sprintf("config file (default is %s)", config.DefaultConfigPath)) + {{- end }} rootCmd.PersistentFlags().String("var", "ViperTest var", "use Viper for configuration") viper.BindPFlag("var", rootCmd.PersistentFlags().Lookup("var")) rootCmd.AddCommand(subCmd) } func initConfig() { + {{- if .EnableDefaultConfig }} err := config.InitConfig(afero.NewOsFs(), configPath) + {{- else }} + err := config.InitConfig(configPath) + {{- end }} if err != nil { panic(err) } @@ -240,6 +267,7 @@ func init() { var tmplTextConfig = ` package config +{{- if .EnableDefaultConfig }} import ( "fmt" "log" @@ -339,4 +367,162 @@ func InitConfig(fs afero.Fs, configPath string) error { } return viper.ReadInConfig() } +{{- else}} +import ( + "fmt" + "os" + + "github.com/spf13/viper" +) + +func InitConfig(configPath string) error { + if _, err := os.Stat(configPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("InitConfig error: config file (%s) not exist", configPath) + } + return fmt.Errorf("InitConfig error: %s", err.Error()) + } + + viper.SetConfigFile(configPath) + if err := viper.ReadInConfig(); err != nil { + return fmt.Errorf("InitConfig error: %s", err.Error()) + } + return nil +} +{{- end }} +` +var tmplTextLog = ` +package logger + +import ( + "fmt" + "io" + "os" + "path" + "time" + + "github.com/lestrrat/go-file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +func NewLoggerByConfig() (*logrus.Logger, error) { + level := viper.GetString("log.level") + stdout := viper.GetBool("log.stdout") + format := viper.GetString("log.format") + logdir := viper.GetString("log.logdir") + logfile := viper.GetString("log.logfile") + if logdir == "" { + logdir = "./log" + } + if logfile == "" { + logfile = "log" + } + fmt.Println(level, stdout, format, logdir, logfile) + return NewLogger(level, format, stdout, logdir, logfile) +} + +func newloggerErr(format string, a ...interface{}) error { + return fmt.Errorf("NewLogger error: %s", fmt.Sprintf(format, a...)) +} + +func NewLogger(loglevel, format string, stdout bool, logdir, logfile string) (*logrus.Logger, error) { + var logger = logrus.New() + var level = logrus.DebugLevel + switch loglevel { + case "panic": + level = logrus.PanicLevel + case "fatal": + level = logrus.FatalLevel + case "error": + level = logrus.ErrorLevel + case "warn": + level = logrus.WarnLevel + case "info": + level = logrus.InfoLevel + case "debug": + level = logrus.DebugLevel + case "trace": + level = logrus.TraceLevel + default: + return logger, newloggerErr("NewLogger error log level not allow: %s allow: panic fatal error warn info debug trace, current:", loglevel) + } + logger.SetLevel(level) + + fileSrc, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend) + if err != nil { + return logger, newloggerErr(err.Error()) + } + var src io.Writer + if stdout { + stdoutSrc := os.Stdout + src = io.MultiWriter(fileSrc, stdoutSrc) + } else { + src = io.MultiWriter(fileSrc) + } + logger.SetOutput(src) + + logger.SetFormatter(getformatter(format)) + logger.AddHook(getLogHook(format, logdir, logfile)) + return logger, nil +} + +func getformatter(f string) logrus.Formatter { + switch f { + case "text": + return &logrus.TextFormatter{ + ForceColors: true, + FullTimestamp: true, + } + case "text_disable_fulltimestamp": + return &logrus.TextFormatter{ + ForceColors: true, + FullTimestamp: false, + } + case "json": + fallthrough + default: + return &logrus.JSONFormatter{} + } + +} + +func getLogHook(format, logdir, logfile string) *lfshook.LfsHook { + logWriter, _ := getLogWriter(logdir, logfile) + writeMap := lfshook.WriterMap{ + logrus.TraceLevel: logWriter, + logrus.InfoLevel: logWriter, + logrus.FatalLevel: logWriter, + logrus.DebugLevel: logWriter, + logrus.WarnLevel: logWriter, + logrus.ErrorLevel: logWriter, + logrus.PanicLevel: logWriter, + } + return lfshook.NewHook(writeMap, getformatter(format)) +} + +func getLogWriter(logdir, logfile string) (*rotatelogs.RotateLogs, error) { + if _, err := os.Stat(logdir); err != nil { + if os.IsNotExist(err) { + err := os.Mkdir(logdir, os.ModePerm) + if err != nil { + return nil, newloggerErr("create log dir (%s) error %s", logdir, err.Error()) + } + } + return nil, newloggerErr(err.Error()) + } + + filepath := path.Join(logdir, logfile) + logWriter, err := rotatelogs.New( + filepath+".%Y%m%d.log", + rotatelogs.WithLinkName(filepath), + rotatelogs.WithMaxAge(7*24*time.Hour), + rotatelogs.WithRotationTime(24*time.Hour), + ) + if err != nil { + return nil, newloggerErr(err.Error()) + } + return logWriter, nil +} ` diff --git a/readme.md b/readme.md index 7d4a6f8..ec6c8f4 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,8 @@ Usage: readygo module_name out_dir [flags] Flags: - -h, --help help for readygo - --without-config Not use configuration files + --enable-default-config generate default config in XDG_CONFIG_HOME, if config file not exist + -h, --help help for readygo + --with-out-config Not use configuration files + --with-out-log Not generate logger ``` \ No newline at end of file