github.com/matrixorigin/matrixone@v1.2.0/pkg/logutil/internal.go (about)

     1  // Copyright 2021 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package logutil
    16  
    17  import (
    18  	"context"
    19  	"os"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"go.uber.org/zap"
    25  	"go.uber.org/zap/zapcore"
    26  	"gopkg.in/natefinch/lumberjack.v2"
    27  )
    28  
    29  // SetupMOLogger sets up the global logger for MO Server.
    30  func SetupMOLogger(conf *LogConfig) {
    31  	setGlobalLogConfig(conf) // keep for dragonboat/v4, ... and so on
    32  	logger, err := initMOLogger(conf)
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  	replaceGlobalLogger(logger)
    37  	Infof("MO logger init, level=%s, log file=%s, stackLevel=%s", conf.Level, conf.Filename, conf.StacktraceLevel)
    38  }
    39  
    40  // initMOLogger initializes a zap Logger.
    41  func initMOLogger(cfg *LogConfig) (*zap.Logger, error) {
    42  	return GetLoggerWithOptions(cfg.getLevel(), cfg.getEncoder(), cfg.getSyncer(), cfg.getOptions()...), nil
    43  }
    44  
    45  // global zap logger for MO server.
    46  var _globalLogger atomic.Value
    47  var _skip1Logger atomic.Value
    48  var _errorLogger atomic.Value
    49  
    50  // init initializes a default zap logger before set up logger.
    51  func init() {
    52  	SetLogReporter(&TraceReporter{noopReportZap, noopContextField})
    53  	conf := GetDefaultConfig()
    54  	setGlobalLogConfig(&conf)
    55  	logger, _ := initMOLogger(&conf)
    56  	replaceGlobalLogger(logger)
    57  }
    58  
    59  func GetDefaultConfig() LogConfig {
    60  	return LogConfig{Level: "info", Format: "console", StacktraceLevel: "panic"}
    61  }
    62  
    63  // GetGlobalLogger returns the current global zap Logger.
    64  func GetGlobalLogger() *zap.Logger {
    65  	return _globalLogger.Load().(*zap.Logger)
    66  }
    67  
    68  func GetSkip1Logger() *zap.Logger {
    69  	return _skip1Logger.Load().(*zap.Logger)
    70  }
    71  
    72  func GetErrorLogger() *zap.Logger {
    73  	return _errorLogger.Load().(*zap.Logger)
    74  }
    75  
    76  // replaceGlobalLogger replaces the current global zap Logger.
    77  func replaceGlobalLogger(logger *zap.Logger) {
    78  	_globalLogger.Store(logger)
    79  	_skip1Logger.Store(logger.WithOptions(zap.AddCallerSkip(1)))
    80  	_errorLogger.Store(logger.WithOptions(zap.AddCallerSkip(1)))
    81  }
    82  
    83  type LogConfig struct {
    84  	Level      string `toml:"level" user_setting:"basic"`
    85  	Format     string `toml:"format" user_setting:"basic"`
    86  	Filename   string `toml:"filename" user_setting:"advanced"`
    87  	MaxSize    int    `toml:"max-size"`
    88  	MaxDays    int    `toml:"max-days"`
    89  	MaxBackups int    `toml:"max-backups"`
    90  	// DisableStore ctrl store log into db
    91  	DisableStore bool `toml:"disable-store"`
    92  	// DisableLog ctrl log into console
    93  	DisableLog bool `toml:"disable-log"`
    94  	// StacktraceLevel
    95  	StacktraceLevel string `toml:"stacktrace-level"`
    96  }
    97  
    98  func (cfg *LogConfig) getSyncer() zapcore.WriteSyncer {
    99  	if cfg.Filename == "" || cfg.Filename == "console" {
   100  		return getConsoleSyncer()
   101  	}
   102  
   103  	if stat, err := os.Stat(cfg.Filename); err == nil {
   104  		if stat.IsDir() {
   105  			panic("log file can't be a directory")
   106  		}
   107  	}
   108  
   109  	if cfg.MaxSize == 0 {
   110  		cfg.MaxSize = 512
   111  	}
   112  	// add lumberjack logger
   113  	return zapcore.AddSync(&lumberjack.Logger{
   114  		Filename:   cfg.Filename,
   115  		MaxSize:    cfg.MaxSize,
   116  		MaxAge:     cfg.MaxDays,
   117  		MaxBackups: cfg.MaxBackups,
   118  		LocalTime:  true,
   119  		Compress:   false,
   120  	})
   121  }
   122  
   123  func (cfg *LogConfig) getEncoder() zapcore.Encoder {
   124  	return getLoggerEncoder(cfg.Format)
   125  }
   126  
   127  func (cfg *LogConfig) getLevel() zap.AtomicLevel {
   128  	level := zap.NewAtomicLevel()
   129  	err := level.UnmarshalText([]byte(cfg.Level))
   130  	if err != nil {
   131  		panic(err)
   132  	}
   133  	return level
   134  }
   135  
   136  // getStacktraceLevel return stacktrace level.
   137  // If cfg.StacktraceLevel == "", then return zap.PanicLevel
   138  func (cfg *LogConfig) getStacktraceLevel() (level zapcore.Level) {
   139  	if len(cfg.StacktraceLevel) == 0 {
   140  		return zap.PanicLevel
   141  	}
   142  	err := level.UnmarshalText([]byte(cfg.StacktraceLevel))
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  	return level
   147  }
   148  
   149  func (cfg *LogConfig) getSinks() (sinks []ZapSink) {
   150  	if !cfg.DisableLog {
   151  		encoder, syncer := cfg.getEncoder(), cfg.getSyncer()
   152  		sinks = append(sinks, ZapSink{encoder, syncer})
   153  	}
   154  	if !cfg.DisableStore {
   155  		encoder, syncer := getTraceLogSinks()
   156  		sinks = append(sinks, ZapSink{encoder, syncer})
   157  	}
   158  	return
   159  }
   160  
   161  func (cfg *LogConfig) getOptions() []zap.Option {
   162  	return []zap.Option{zap.AddStacktrace(cfg.getStacktraceLevel()), zap.AddCaller()}
   163  }
   164  
   165  func getLoggerEncoder(format string) zapcore.Encoder {
   166  	encoderConfig := zapcore.EncoderConfig{
   167  		MessageKey:    "msg",
   168  		LevelKey:      "level",
   169  		TimeKey:       "time",
   170  		NameKey:       "name",
   171  		CallerKey:     "caller",
   172  		StacktraceKey: "stacktrace",
   173  		LineEnding:    zapcore.DefaultLineEnding,
   174  		EncodeLevel:   zapcore.CapitalLevelEncoder,
   175  		EncodeTime: zapcore.TimeEncoder(func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
   176  			enc.AppendString(t.Format("2006/01/02 15:04:05.000000 -0700"))
   177  		}),
   178  		EncodeDuration:   zapcore.StringDurationEncoder,
   179  		EncodeCaller:     zapcore.ShortCallerEncoder,
   180  		ConsoleSeparator: " ",
   181  	}
   182  
   183  	switch format {
   184  	case "json", "":
   185  		return zapcore.NewJSONEncoder(encoderConfig)
   186  	case "console":
   187  		return zapcore.NewConsoleEncoder(encoderConfig)
   188  	default:
   189  		panic(moerr.NewInternalError(context.Background(), "unsupported log format: %s", format))
   190  	}
   191  }
   192  
   193  func getConsoleSyncer() zapcore.WriteSyncer {
   194  	syncer, _, err := zap.Open([]string{"stdout"}...)
   195  	if err != nil {
   196  		panic(err)
   197  	}
   198  	return syncer
   199  }