diff --git a/internal/dazBlog/dazBlog.go b/internal/dazBlog/dazBlog.go index 70f1198..7497591 100644 --- a/internal/dazBlog/dazBlog.go +++ b/internal/dazBlog/dazBlog.go @@ -15,6 +15,8 @@ import ( var cfgFile string +// NewDazBlogCommand create the *cobra.Command object +// so can use the Execute method to start func NewDazBlogCommand() *cobra.Command { cmd := &cobra.Command{ // specify the name of the command @@ -33,9 +35,10 @@ Find more dBlog information at: RunE: func(cmd *cobra.Command, args []string) error { log.Init(logOptions()) defer log.Sync() + return run() }, - // + // no need to specify command line parameters Args: func(cmd *cobra.Command, args []string) error { for _, arg := range args { if len(arg) > 0 { @@ -47,9 +50,18 @@ Find more dBlog information at: }, } + // The following settings make the initConfig function be called + // every time when the command is executed to read the configuration. cobra.OnInitialize(initConfig) - cmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "The path to the miniblog configuration file. Empty string for no configuration file.") + // define custom flag and config + + // cobra support persistent flag + // for providing options that work across all sub-commands of a command + cmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "The path to the dBlog configuration file. Empty string for no configuration file.") + + // cobra supports local flag + // which can only be used within the command to which they are bound cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") return cmd } diff --git a/internal/dazBlog/helper.go b/internal/dazBlog/helper.go index 5d53afa..918070d 100644 --- a/internal/dazBlog/helper.go +++ b/internal/dazBlog/helper.go @@ -15,36 +15,52 @@ import ( ) const ( - recommendedHomeDir = ".dazblog " - defaultConfigName = "dazBlog.yaml" + // recommendedHomeDir defines the default directory for storing dBlog service configurations + recommendedHomeDir = ".dblog " + // defaultConfigName specifies the default configuration file name for dBlog service + defaultConfigName = "dazBlog.yaml" ) +// initConfig set the config file name to be read, env variable, and read config file content into viper func initConfig() { if cfgFile != "" { + // read config file from the specified file viper.SetConfigFile(cfgFile) } else { + // search user's homedir home, err := os.UserHomeDir() + // if failed, print 'Error: xxx' and do exit(1) cobra.CheckErr(err) + // add `$HOME/` into search path viper.AddConfigPath(filepath.Join(home, recommendedHomeDir)) + // add current dir into search path viper.AddConfigPath(".") + // set the config file's type viper.SetConfigType("yaml") + // set the config file's name viper.SetConfigName(defaultConfigName) } + // read matching env variable viper.AutomaticEnv() + // set env variable's prefix viper.SetEnvPrefix("MINIBLOG") - replacer := strings.NewReplacer(".", "-") + // Replace '.' and '-' with '_' in the key string before calling viper.Get(key). + replacer := strings.NewReplacer(".", "_", "-", "_") viper.SetEnvKeyReplacer(replacer) + // read config file if err := viper.ReadInConfig(); err != nil { log.Errorw("Failed to read config file", "err", err) } + // print the currently used config file log.Infow("Using config file", "file", viper.ConfigFileUsed()) } +// logOptions reads log config from viper func logOptions() *log.Options { return &log.Options{ DisableCaller: viper.GetBool("log.disable-caller"), diff --git a/internal/pkg/log/log.go b/internal/pkg/log/log.go index d92557c..5380cdd 100644 --- a/internal/pkg/log/log.go +++ b/internal/pkg/log/log.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. The original repo for // this file is https://github.com/Daz-3ux/dBlog. +// Package log is a log package used by dazBlog project package log import ( @@ -30,6 +31,7 @@ type zapLogger struct { z *zap.Logger } +// ensure that zapLogger implements the Logger interface var _ Logger = (*zapLogger)(nil) var ( @@ -53,21 +55,33 @@ func NewLogger(opts *Options) *zapLogger { opts = NewOptions() } + // convert text-based log level, e.g. "info", to the zapcore.Level type var zapLevel zapcore.Level if err := zapLevel.UnmarshalText([]byte(opts.Level)); err != nil { + // if specify an invalid log level, use info level as default zapLevel = zapcore.InfoLevel } + // create a default zapcore.EncoderConfig encoderConfig := zap.NewProductionEncoderConfig() + + // customize the default zapcore.EncoderConfig + // customize MessageKey to "message" for a more explicit meaning encoderConfig.MessageKey = "message" + // customize TimeKey to "timestamp" for a more explicit meaning encoderConfig.TimeKey = "timestamp" + // customize Level style to Capital and Color + encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + // customize Time format for improved readability encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) } + // customize Duration format for improved precision encoderConfig.EncodeDuration = func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { enc.AppendFloat64(float64(d) / float64(time.Millisecond)) } + // cfg the config required to build a zap.Logger cfg := &zap.Config{ DisableCaller: opts.DisableCaller, DisableStacktrace: opts.DisableStacktrace, @@ -75,9 +89,11 @@ func NewLogger(opts *Options) *zapLogger { Encoding: opts.Format, EncoderConfig: encoderConfig, OutputPaths: opts.OutputPaths, - ErrorOutputPaths: []string{"stderr"}, + // specify the default inner error output path + ErrorOutputPaths: []string{"stderr"}, } + // use cfg to build a *zap.Logger z, err := cfg.Build(zap.AddStacktrace(zap.PanicLevel), zap.AddCallerSkip(1)) if err != nil { panic(err) @@ -86,11 +102,14 @@ func NewLogger(opts *Options) *zapLogger { // OOP: wrap the zap library in a custom struct logger := &zapLogger{z: z} + // redirect the standard library's log to the zap logger zap.RedirectStdLog(z) return logger } +// Sync flushes all buffered logs into disk +// main function should call this function before exit func Sync() { std.Sync() } @@ -99,6 +118,7 @@ func (l *zapLogger) Sync() { _ = l.z.Sync() } +// Debugw print debug level log func Debugw(msg string, keysAndValues ...interface{}) { std.z.Sugar().Debugw(msg, keysAndValues...) } @@ -107,6 +127,7 @@ func (l *zapLogger) Debugw(msg string, keysAndValues ...interface{}) { l.z.Sugar().Debugw(msg, keysAndValues...) } +// Infow print info level log func Infow(msg string, keysAndValues ...interface{}) { std.z.Sugar().Infow(msg, keysAndValues...) } @@ -115,6 +136,7 @@ func (l *zapLogger) Infow(msg string, keysAndValues ...interface{}) { l.z.Sugar().Infow(msg, keysAndValues...) } +// Warnw print warn level log func Warnw(msg string, keysAndValues ...interface{}) { std.z.Sugar().Warnw(msg, keysAndValues...) } @@ -123,6 +145,7 @@ func (l *zapLogger) Warnw(msg string, keysAndValues ...interface{}) { l.z.Sugar().Warnw(msg, keysAndValues...) } +// Errorw print error level log func Errorw(msg string, keysAndValues ...interface{}) { std.z.Sugar().Errorw(msg, keysAndValues...) } @@ -131,6 +154,7 @@ func (l *zapLogger) Errorw(msg string, keysAndValues ...interface{}) { l.z.Sugar().Errorw(msg, keysAndValues...) } +// Panicw print panic level log func Panicw(msg string, keysAndValues ...interface{}) { std.z.Sugar().Panicw(msg, keysAndValues...) } @@ -139,6 +163,7 @@ func (l *zapLogger) Panicw(msg string, keysAndValues ...interface{}) { l.z.Sugar().Panicw(msg, keysAndValues...) } +// Fatalw print fatal level log func Fatalw(msg string, keysAndValues ...interface{}) { std.z.Sugar().Fatalw(msg, keysAndValues...) } diff --git a/internal/pkg/log/options.go b/internal/pkg/log/options.go index 443aafa..91be284 100644 --- a/internal/pkg/log/options.go +++ b/internal/pkg/log/options.go @@ -11,13 +11,19 @@ import ( // Options the options for the logger type Options struct { - DisableCaller bool + // if enabled, the log will display the file and line num of the calling log + DisableCaller bool + // if enabled, will print stack info for panic and higher log level DisableStacktrace bool - Level string - Format string - OutputPaths []string + // log level: debug, info , warn, error, dpanic, panic, fatal + Level string + // log format: console, json + Format string + // specify log output path + OutputPaths []string } +// NewOptions creates an Options object with default parameters func NewOptions() *Options { return &Options{ DisableCaller: false,