blob: 2ea41f3de4257eb125007c4a52cb7158d0927852 [file] [log] [blame]
// 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, ",")
}