diff --git a/conf/config.toml b/conf/config.toml index 71f1df06986..9e7ab4517b1 100644 --- a/conf/config.toml +++ b/conf/config.toml @@ -20,8 +20,21 @@ tso-save-interval = "3s" [log] level = "info" -#[log.file] +# log format, one of json, text, console +#format = "text" + +# disable automatic timestamps in output +#disable-timestamp = false + +# file logging +[log.file] #filename = "" +# max log file size in MB +#max-size = 300 +# max log file keep days +#max-days = 28 +# maximum number of old log files to retain +#max-backups = 7 [metric] # prometheus client push interval, set "0s" to disable prometheus. diff --git a/pkg/logutil/log.go b/pkg/logutil/log.go index 630d293f3cc..2e0000f7206 100644 --- a/pkg/logutil/log.go +++ b/pkg/logutil/log.go @@ -30,23 +30,33 @@ import ( const ( defaultLogTimeFormat = "2006/01/02 15:04:05" defaultLogMaxSize = 300 // MB - defaultLogMaxBackups = 3 - defaultLogMaxAge = 28 // days + defaultLogFormat = "text" defaultLogLevel = log.InfoLevel - - logDirMode = 0755 ) // FileLogConfig serializes file log related config in toml/json. type FileLogConfig struct { + // Log filename, leave empty to disable file log. Filename string `toml:"filename" json:"filename"` + // Is log rotate enabled. TODO. + LogRotate bool `toml:"log-rotate" json:"log-rotate"` + // Max size for a single file, in MB. + MaxSize int `toml:"max-size" json:"max-size"` + // Max log keep days, default is never deleting. + MaxDays int `toml:"max-days" json:"max-days"` + // Maximum number of old log files to retain. + MaxBackups int `toml:"max-backups" json:"max-backups"` } // LogConfig serializes log related config in toml/json. type LogConfig struct { // Log level. Level string `toml:"level" json:"level"` - // Log file. + // Log format. one of json, text, or console. + Format string `toml:"format" json:"format"` + // Disable automatic timestamps in output. + DisableTimestamp bool `toml:"disable-timestamp" json:"disable-timestamp"` + // File log config. File FileLogConfig `toml:"file" json:"file"` } @@ -130,7 +140,9 @@ func stringToLogLevel(level string) log.Level { } // textFormatter is for compatability with ngaut/log -type textFormatter struct{} +type textFormatter struct { + DisableTimestamp bool +} // Format implements logrus.Formatter func (f *textFormatter) Format(entry *log.Entry) ([]byte, error) { @@ -140,9 +152,11 @@ func (f *textFormatter) Format(entry *log.Entry) ([]byte, error) { } else { b = &bytes.Buffer{} } - b.WriteString(entry.Time.Format(defaultLogTimeFormat)) + if !f.DisableTimestamp { + fmt.Fprintf(b, "%s ", entry.Time.Format(defaultLogTimeFormat)) + } if file, ok := entry.Data["file"]; ok { - fmt.Fprintf(b, " %s:%v:", file, entry.Data["line"]) + fmt.Fprintf(b, "%s:%v:", file, entry.Data["line"]) } fmt.Fprintf(b, " [%s] %s", entry.Level.String(), entry.Message) for k, v := range entry.Data { @@ -154,33 +168,48 @@ func (f *textFormatter) Format(entry *log.Entry) ([]byte, error) { return b.Bytes(), nil } -// setLogOutput sets output path for all logs. -func setLogOutput(filename string) error { - // PD log - if st, err := os.Stat(filename); err == nil { +func stringToLogFormatter(format string, disableTimestamp bool) log.Formatter { + switch strings.ToLower(format) { + case "text": + return &textFormatter{ + DisableTimestamp: disableTimestamp, + } + case "json": + return &log.JSONFormatter{ + TimestampFormat: defaultLogTimeFormat, + DisableTimestamp: disableTimestamp, + } + case "console": + return &log.TextFormatter{ + FullTimestamp: true, + TimestampFormat: defaultLogTimeFormat, + DisableTimestamp: disableTimestamp, + } + default: + return &textFormatter{} + } +} + +// InitFileLog initializes file based logging options. +func InitFileLog(cfg *FileLogConfig) error { + if st, err := os.Stat(cfg.Filename); err == nil { if st.IsDir() { return errors.New("can't use directory as log file name") } } - dir := path.Dir(filename) - err := os.MkdirAll(dir, logDirMode) - if err != nil { - return errors.Trace(err) + if cfg.MaxSize == 0 { + cfg.MaxSize = defaultLogMaxSize } // use lumberjack to logrotate output := &lumberjack.Logger{ - Filename: filename, - MaxSize: defaultLogMaxSize, // megabytes - MaxBackups: defaultLogMaxBackups, - MaxAge: defaultLogMaxAge, // days + Filename: cfg.Filename, + MaxSize: cfg.MaxSize, + MaxBackups: cfg.MaxBackups, + MaxAge: cfg.MaxDays, LocalTime: true, } - if _, err := output.Write([]byte{}); err != nil { - return errors.Errorf("log file is not writable: %v", err) - } - log.SetOutput(output) return nil } @@ -189,7 +218,11 @@ func setLogOutput(filename string) error { func InitLogger(cfg *LogConfig) error { log.SetLevel(stringToLogLevel(cfg.Level)) log.AddHook(&contextHook{}) - log.SetFormatter(&textFormatter{}) + + if cfg.Format == "" { + cfg.Format = defaultLogFormat + } + log.SetFormatter(stringToLogFormatter(cfg.Format, cfg.DisableTimestamp)) // etcd log capnslog.SetFormatter(&redirectFormatter{}) @@ -198,7 +231,7 @@ func InitLogger(cfg *LogConfig) error { return nil } - err := setLogOutput(cfg.File.Filename) + err := InitFileLog(&cfg.File) if err != nil { return errors.Trace(err) }