| // Licensed to Apache Software Foundation (ASF) under one or more contributor |
| // license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright |
| // ownership. Apache Software Foundation (ASF) licenses this file to you under |
| // the Apache License, Version 2.0 (the "License"); you may |
| // not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package log |
| |
| import ( |
| "fmt" |
| "os" |
| "strings" |
| "sync" |
| |
| "github.com/sirupsen/logrus" |
| ) |
| |
| // Default logger config. |
| const ( |
| defaultLogPattern = "%time [%level][%field] - %msg" |
| defaultTimePattern = "2006-01-02 15:04:05.000" |
| ) |
| |
| // LoggerConfig initializes the global logger config. |
| type LoggerConfig struct { |
| LogPattern string `mapstructure:"log_pattern"` |
| TimePattern string `mapstructure:"time_pattern"` |
| Level string `mapstructure:"level"` |
| } |
| |
| // FormatOption is a function to set formatter config. |
| type FormatOption func(f *formatter) |
| |
| // FormatOption is a function to set logger config. |
| type ConfigOption func(l *logrus.Logger) |
| |
| type formatter struct { |
| logPattern string |
| timePattern string |
| } |
| |
| // Logger is the global logger. |
| var Logger *logrus.Logger |
| var once sync.Once |
| |
| func Init(cfg *LoggerConfig) { |
| once.Do(func() { |
| var configOpts []ConfigOption |
| var formatOpts []FormatOption |
| |
| if cfg.Level != "" { |
| configOpts = append(configOpts, SetLevel(cfg.Level)) |
| } else { |
| configOpts = append(configOpts, SetLevel(logrus.InfoLevel.String())) |
| } |
| if cfg.TimePattern != "" { |
| formatOpts = append(formatOpts, SetTimePattern(cfg.TimePattern)) |
| } else { |
| formatOpts = append(formatOpts, SetTimePattern(defaultTimePattern)) |
| } |
| if cfg.LogPattern != "" { |
| formatOpts = append(formatOpts, SetLogPattern(cfg.LogPattern)) |
| } else { |
| formatOpts = append(formatOpts, SetLogPattern(defaultLogPattern)) |
| } |
| initBySettings(configOpts, formatOpts) |
| }) |
| } |
| |
| // The Logger init method, keep Logger as a singleton. |
| func initBySettings(configOpts []ConfigOption, formatOpts []FormatOption) { |
| // Default Logger. |
| Logger = logrus.New() |
| Logger.SetOutput(os.Stdout) |
| for _, opt := range configOpts { |
| opt(Logger) |
| } |
| // Default formatter. |
| f := &formatter{} |
| for _, opt := range formatOpts { |
| opt(f) |
| } |
| if !strings.Contains(f.logPattern, "\n") { |
| f.logPattern += "\n" |
| } |
| Logger.SetFormatter(f) |
| } |
| |
| // Put the log pattern in formatter. |
| func SetLogPattern(logPattern string) FormatOption { |
| return func(f *formatter) { |
| f.logPattern = logPattern |
| } |
| } |
| |
| // Put the time pattern in formatter. |
| func SetTimePattern(timePattern string) FormatOption { |
| return func(f *formatter) { |
| f.timePattern = timePattern |
| } |
| } |
| |
| // Put the time pattern in formatter. |
| func SetLevel(levelStr string) ConfigOption { |
| return func(logger *logrus.Logger) { |
| level, err := logrus.ParseLevel(levelStr) |
| if err != nil { |
| fmt.Printf("logger level does not exist: %s, level would be set info", levelStr) |
| level = logrus.InfoLevel |
| } |
| logger.SetLevel(level) |
| } |
| } |
| |
| // Format supports unified log output format that has %time, %level, %field and %msg. |
| func (f *formatter) Format(entry *logrus.Entry) ([]byte, error) { |
| output := f.logPattern |
| output = strings.Replace(output, "%time", entry.Time.Format(f.timePattern), 1) |
| output = strings.Replace(output, "%level", entry.Level.String(), 1) |
| output = strings.Replace(output, "%field", buildFields(entry), 1) |
| output = strings.Replace(output, "%msg", entry.Message, 1) |
| return []byte(output), nil |
| } |
| |
| func buildFields(entry *logrus.Entry) string { |
| var fields []string |
| for key, val := range entry.Data { |
| stringVal, ok := val.(string) |
| if !ok { |
| stringVal = fmt.Sprint(val) |
| } |
| fields = append(fields, key+"="+stringVal) |
| } |
| return strings.Join(fields, ",") |
| } |