2021-11-03 19:23:28 +08:00
|
|
|
// Licensed to the LF AI & Data foundation under one
|
|
|
|
// or more contributor license agreements. See the NOTICE file
|
|
|
|
// distributed with this work for additional information
|
|
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
|
|
// to you under the Apache License, Version 2.0 (the
|
|
|
|
// "License"); you may not use this file except in compliance
|
2021-09-07 16:07:58 +08:00
|
|
|
// with the License. You may obtain a copy of the License at
|
|
|
|
//
|
2021-11-03 19:23:28 +08:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2021-09-07 16:07:58 +08:00
|
|
|
//
|
2021-11-03 19:23:28 +08:00
|
|
|
// 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.
|
2021-09-07 16:07:58 +08:00
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
// Copyright 2019 PingCAP, Inc.
|
|
|
|
//
|
|
|
|
// Licensed 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,
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
2021-03-30 20:19:30 +08:00
|
|
|
"fmt"
|
2021-02-19 15:37:04 +08:00
|
|
|
"os"
|
2023-02-14 16:56:34 +08:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2022-08-23 10:44:52 +08:00
|
|
|
"sync"
|
2021-02-19 15:37:04 +08:00
|
|
|
"sync/atomic"
|
|
|
|
|
2023-02-26 11:31:49 +08:00
|
|
|
"github.com/cockroachdb/errors"
|
2021-10-15 11:46:33 +08:00
|
|
|
"github.com/uber/jaeger-client-go/utils"
|
2021-02-19 15:37:04 +08:00
|
|
|
"go.uber.org/zap"
|
|
|
|
"go.uber.org/zap/zapcore"
|
2021-09-06 16:50:42 +08:00
|
|
|
"go.uber.org/zap/zaptest"
|
2023-04-06 19:14:32 +08:00
|
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
2021-02-19 15:37:04 +08:00
|
|
|
)
|
|
|
|
|
2021-10-15 11:46:33 +08:00
|
|
|
var _globalL, _globalP, _globalS, _globalR atomic.Value
|
2022-08-23 10:44:52 +08:00
|
|
|
|
|
|
|
var (
|
|
|
|
_globalLevelLogger sync.Map
|
2023-01-13 14:11:40 +08:00
|
|
|
_namedRateLimiters sync.Map
|
2022-08-23 10:44:52 +08:00
|
|
|
)
|
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
func init() {
|
|
|
|
l, p := newStdLogger()
|
2022-08-23 10:44:52 +08:00
|
|
|
|
|
|
|
replaceLeveledLoggers(l)
|
2021-02-19 15:37:04 +08:00
|
|
|
_globalL.Store(l)
|
|
|
|
_globalP.Store(p)
|
2022-08-23 10:44:52 +08:00
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
s := _globalL.Load().(*zap.Logger).Sugar()
|
|
|
|
_globalS.Store(s)
|
2021-10-15 11:46:33 +08:00
|
|
|
|
|
|
|
r := utils.NewRateLimiter(1.0, 60.0)
|
|
|
|
_globalR.Store(r)
|
2021-02-19 15:37:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// InitLogger initializes a zap logger.
|
|
|
|
func InitLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
|
2023-02-14 16:56:34 +08:00
|
|
|
var outputs []zapcore.WriteSyncer
|
2021-02-19 15:37:04 +08:00
|
|
|
if len(cfg.File.Filename) > 0 {
|
|
|
|
lg, err := initFileLog(&cfg.File)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2023-02-14 16:56:34 +08:00
|
|
|
outputs = append(outputs, zapcore.AddSync(lg))
|
|
|
|
}
|
|
|
|
if cfg.Stdout {
|
2021-02-19 15:37:04 +08:00
|
|
|
stdOut, _, err := zap.Open([]string{"stdout"}...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2023-02-14 16:56:34 +08:00
|
|
|
outputs = append(outputs, stdOut)
|
2021-02-19 15:37:04 +08:00
|
|
|
}
|
2022-08-23 10:44:52 +08:00
|
|
|
debugCfg := *cfg
|
|
|
|
debugCfg.Level = "debug"
|
2023-02-14 16:56:34 +08:00
|
|
|
outputsWriter := zap.CombineWriteSyncers(outputs...)
|
|
|
|
debugL, r, err := InitLoggerWithWriteSyncer(&debugCfg, outputsWriter, opts...)
|
2022-08-23 10:44:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
replaceLeveledLoggers(debugL)
|
|
|
|
level := zapcore.DebugLevel
|
|
|
|
if err := level.UnmarshalText([]byte(cfg.Level)); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
r.Level.SetLevel(level)
|
|
|
|
return debugL.WithOptions(zap.IncreaseLevel(level), zap.AddCallerSkip(1)), r, nil
|
2021-02-19 15:37:04 +08:00
|
|
|
}
|
|
|
|
|
2021-09-07 16:07:58 +08:00
|
|
|
// InitTestLogger initializes a logger for unit tests
|
2021-09-06 16:50:42 +08:00
|
|
|
func InitTestLogger(t zaptest.TestingT, cfg *Config, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
|
|
|
|
writer := newTestingWriter(t)
|
|
|
|
zapOptions := []zap.Option{
|
|
|
|
// Send zap errors to the same writer and mark the test as failed if
|
|
|
|
// that happens.
|
|
|
|
zap.ErrorOutput(writer.WithMarkFailed(true)),
|
|
|
|
}
|
|
|
|
opts = append(zapOptions, opts...)
|
|
|
|
return InitLoggerWithWriteSyncer(cfg, writer, opts...)
|
|
|
|
}
|
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
// InitLoggerWithWriteSyncer initializes a zap logger with specified write syncer.
|
|
|
|
func InitLoggerWithWriteSyncer(cfg *Config, output zapcore.WriteSyncer, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
|
|
|
|
level := zap.NewAtomicLevel()
|
|
|
|
err := level.UnmarshalText([]byte(cfg.Level))
|
|
|
|
if err != nil {
|
2021-12-09 19:43:39 +08:00
|
|
|
return nil, nil, fmt.Errorf("initLoggerWithWriteSyncer UnmarshalText cfg.Level err:%w", err)
|
2021-02-19 15:37:04 +08:00
|
|
|
}
|
|
|
|
core := NewTextCore(newZapTextEncoder(cfg), output, level)
|
|
|
|
opts = append(cfg.buildOptions(output), opts...)
|
|
|
|
lg := zap.New(core, opts...)
|
|
|
|
r := &ZapProperties{
|
|
|
|
Core: core,
|
|
|
|
Syncer: output,
|
|
|
|
Level: level,
|
|
|
|
}
|
|
|
|
return lg, r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// initFileLog initializes file based logging options.
|
|
|
|
func initFileLog(cfg *FileLogConfig) (*lumberjack.Logger, error) {
|
2023-02-14 16:56:34 +08:00
|
|
|
logPath := strings.Join([]string{cfg.RootPath, cfg.Filename}, string(filepath.Separator))
|
|
|
|
if st, err := os.Stat(logPath); err == nil {
|
2021-02-19 15:37:04 +08:00
|
|
|
if st.IsDir() {
|
|
|
|
return nil, errors.New("can't use directory as log file name")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if cfg.MaxSize == 0 {
|
|
|
|
cfg.MaxSize = defaultLogMaxSize
|
|
|
|
}
|
|
|
|
|
|
|
|
// use lumberjack to logrotate
|
|
|
|
return &lumberjack.Logger{
|
2023-02-14 16:56:34 +08:00
|
|
|
Filename: logPath,
|
2021-02-19 15:37:04 +08:00
|
|
|
MaxSize: cfg.MaxSize,
|
|
|
|
MaxBackups: cfg.MaxBackups,
|
|
|
|
MaxAge: cfg.MaxDays,
|
|
|
|
LocalTime: true,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStdLogger() (*zap.Logger, *ZapProperties) {
|
2023-07-18 12:53:21 +08:00
|
|
|
conf := &Config{Level: "debug", Stdout: true, DisableErrorVerbose: true}
|
2023-09-01 13:09:01 +08:00
|
|
|
lg, r, _ := InitLogger(conf, zap.OnFatal(zapcore.WriteThenPanic))
|
2021-02-19 15:37:04 +08:00
|
|
|
return lg, r
|
|
|
|
}
|
|
|
|
|
|
|
|
// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
|
|
|
|
// It's safe for concurrent use.
|
|
|
|
func L() *zap.Logger {
|
|
|
|
return _globalL.Load().(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// S returns the global SugaredLogger, which can be reconfigured with
|
|
|
|
// ReplaceGlobals. It's safe for concurrent use.
|
|
|
|
func S() *zap.SugaredLogger {
|
|
|
|
return _globalS.Load().(*zap.SugaredLogger)
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:18:41 +08:00
|
|
|
// R returns utils.ReconfigurableRateLimiter.
|
2021-10-15 11:46:33 +08:00
|
|
|
func R() *utils.ReconfigurableRateLimiter {
|
|
|
|
return _globalR.Load().(*utils.ReconfigurableRateLimiter)
|
|
|
|
}
|
|
|
|
|
2022-08-23 10:44:52 +08:00
|
|
|
func ctxL() *zap.Logger {
|
|
|
|
level := _globalP.Load().(*ZapProperties).Level.Level()
|
|
|
|
l, ok := _globalLevelLogger.Load(level)
|
|
|
|
if !ok {
|
|
|
|
return L()
|
|
|
|
}
|
|
|
|
return l.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func debugL() *zap.Logger {
|
|
|
|
v, _ := _globalLevelLogger.Load(zapcore.DebugLevel)
|
|
|
|
return v.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func infoL() *zap.Logger {
|
|
|
|
v, _ := _globalLevelLogger.Load(zapcore.InfoLevel)
|
|
|
|
return v.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func warnL() *zap.Logger {
|
|
|
|
v, _ := _globalLevelLogger.Load(zapcore.WarnLevel)
|
|
|
|
return v.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func errorL() *zap.Logger {
|
|
|
|
v, _ := _globalLevelLogger.Load(zapcore.ErrorLevel)
|
|
|
|
return v.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fatalL() *zap.Logger {
|
|
|
|
v, _ := _globalLevelLogger.Load(zapcore.FatalLevel)
|
|
|
|
return v.(*zap.Logger)
|
|
|
|
}
|
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
// ReplaceGlobals replaces the global Logger and SugaredLogger.
|
|
|
|
// It's safe for concurrent use.
|
|
|
|
func ReplaceGlobals(logger *zap.Logger, props *ZapProperties) {
|
|
|
|
_globalL.Store(logger)
|
|
|
|
_globalS.Store(logger.Sugar())
|
|
|
|
_globalP.Store(props)
|
|
|
|
}
|
|
|
|
|
2022-08-23 10:44:52 +08:00
|
|
|
func replaceLeveledLoggers(debugLogger *zap.Logger) {
|
2023-09-21 09:45:27 +08:00
|
|
|
levels := []zapcore.Level{
|
|
|
|
zapcore.DebugLevel, zapcore.InfoLevel, zapcore.WarnLevel, zapcore.ErrorLevel,
|
|
|
|
zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel,
|
|
|
|
}
|
2022-08-23 10:44:52 +08:00
|
|
|
for _, level := range levels {
|
|
|
|
levelL := debugLogger.WithOptions(zap.IncreaseLevel(level))
|
|
|
|
_globalLevelLogger.Store(level, levelL)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 15:37:04 +08:00
|
|
|
// Sync flushes any buffered log entries.
|
|
|
|
func Sync() error {
|
2022-08-23 10:44:52 +08:00
|
|
|
if err := L().Sync(); err != nil {
|
2021-02-19 15:37:04 +08:00
|
|
|
return err
|
|
|
|
}
|
2022-08-23 10:44:52 +08:00
|
|
|
if err := S().Sync(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var reterr error
|
|
|
|
_globalLevelLogger.Range(func(key, val interface{}) bool {
|
|
|
|
l := val.(*zap.Logger)
|
|
|
|
if err := l.Sync(); err != nil {
|
|
|
|
reterr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
return reterr
|
2021-02-19 15:37:04 +08:00
|
|
|
}
|
2022-10-13 10:37:23 +08:00
|
|
|
|
|
|
|
func Level() zap.AtomicLevel {
|
|
|
|
return _globalP.Load().(*ZapProperties).Level
|
|
|
|
}
|