* Copyright (c) 2024 Huawei Technologies Co., Ltd.
* openFuyao is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package zlog
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
const (
defaultConfigPath = "/etc/volcano-config-service"
defaultConfigName = "volcano-config-service"
defaultConfigType = "yaml"
defaultLogPath = "/var/log/volcano-config-service"
)
var Logger *zap.SugaredLogger
var logLevel = map[string]zapcore.Level{
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel,
"error": zapcore.ErrorLevel,
}
var watchOnce = sync.Once{}
type LogConfig struct {
Level string
EncoderType string
Path string
FileName string
MaxSize int
MaxBackups int
MaxAge int
LocalTime bool
Compress bool
OutMod string
}
func init() {
var conf *LogConfig
var err error
if conf, err = loadConfig(); err != nil {
fmt.Printf("loadConfig fail err is %v. use DefaultConf\n", err)
conf = getDefaultConf()
}
Logger = GetLogger(conf)
}
func loadConfig() (*LogConfig, error) {
viper.AddConfigPath(defaultConfigPath)
viper.SetConfigName(defaultConfigName)
viper.SetConfigType(defaultConfigType)
config, err := parseConfig()
if err != nil {
return nil, err
}
watchConfig()
return config, nil
}
func getDefaultConf() *LogConfig {
var defaultConf = &LogConfig{
Level: "info",
EncoderType: "console",
Path: defaultLogPath,
FileName: "volcano-config-service.log",
MaxSize: 20,
MaxBackups: 5,
MaxAge: 30,
LocalTime: false,
Compress: true,
OutMod: "both",
}
exePath, err := os.Executable()
if err != nil {
return defaultConf
}
serviceName := strings.TrimSuffix(filepath.Base(exePath), filepath.Ext(filepath.Base(exePath)))
defaultConf.Path = filepath.Join(defaultLogPath, serviceName)
return defaultConf
}
func GetLogger(conf *LogConfig) *zap.SugaredLogger {
writeSyncer := getLogWriter(conf)
encoder := getEncoder(conf)
level, ok := logLevel[strings.ToLower(conf.Level)]
if !ok {
level = logLevel["info"]
}
core := zapcore.NewCore(encoder, writeSyncer, level)
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
return logger.Sugar()
}
func watchConfig() {
watchOnce.Do(func() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
Logger.Warn("ConfigClient file changed")
conf, err := parseConfig()
if err != nil {
Logger.Warnf("Error reloading config file: %v\n", err)
} else {
Logger = GetLogger(conf)
}
})
})
}
func parseConfig() (*LogConfig, error) {
err := viper.ReadInConfig()
if err != nil {
return nil, err
}
var config LogConfig
err = viper.Unmarshal(&config)
if err != nil {
return nil, err
}
return &config, nil
}
func getEncoder(conf *LogConfig) zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
if strings.ToLower(conf.EncoderType) == "json" {
return zapcore.NewJSONEncoder(encoderConfig)
}
return zapcore.NewConsoleEncoder(encoderConfig)
}
func getLogWriter(conf *LogConfig) zapcore.WriteSyncer {
switch conf.OutMod {
case "console":
return zapcore.AddSync(os.Stdout)
case "file":
return zapcore.AddSync(createLumberjackLogger(conf))
case "both":
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(createLumberjackLogger(conf)), zapcore.AddSync(os.Stdout))
default:
return zapcore.AddSync(os.Stdout)
}
}
func createLumberjackLogger(conf *LogConfig) *lumberjack.Logger {
return &lumberjack.Logger{
Filename: filepath.Join(conf.Path, conf.FileName),
MaxSize: conf.MaxSize,
MaxBackups: conf.MaxBackups,
MaxAge: conf.MaxAge,
LocalTime: conf.LocalTime,
Compress: conf.Compress,
}
}
func Warn(args ...interface{}) {
Logger.Warn(args...)
}
func Info(args ...interface{}) {
Logger.Info(args...)
}
func Error(args ...interface{}) {
Logger.Error(args...)
}
func FormatError(template string, args ...interface{}) {
Logger.Errorf(template, args...)
}
func FormatWarn(template string, args ...interface{}) {
Logger.Warnf(template, args...)
}
func FormatInfo(template string, args ...interface{}) {
Logger.Infof(template, args...)
}
func FormatDebug(template string, args ...interface{}) {
Logger.Debugf(template, args...)
}
func FormatFatal(template string, args ...interface{}) {
Logger.Fatalf(template, args...)
}
func Errorf(template string, args ...interface{}) {
Logger.Errorf(template, args...)
}
func Warnf(template string, args ...interface{}) {
Logger.Warnf(template, args...)
}
func Infof(template string, args ...interface{}) {
Logger.Infof(template, args...)
}
func Debugf(template string, args ...interface{}) {
Logger.Debugf(template, args...)
}
func Fatalf(template string, args ...interface{}) {
Logger.Fatalf(template, args...)
}
func Sync() error {
return Logger.Sync()
}