milvus/internal/log/log.go
Zach 0c9a10e8f8
1. refine logging interfaces (#18692)
2. adjust logs for query/search requests

Signed-off-by: Zach41 <zongmei.zhang@zilliz.com>

Signed-off-by: Zach41 <zongmei.zhang@zilliz.com>
2022-08-23 10:44:52 +08:00

251 lines
6.8 KiB
Go

// 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
// 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.
// 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 (
"fmt"
"net/http"
"os"
"sync"
"sync/atomic"
"errors"
"github.com/uber/jaeger-client-go/utils"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
var _globalL, _globalP, _globalS, _globalR atomic.Value
var (
_globalLevelLogger sync.Map
)
var rateLimiter *utils.ReconfigurableRateLimiter
func init() {
l, p := newStdLogger()
replaceLeveledLoggers(l)
_globalL.Store(l)
_globalP.Store(p)
s := _globalL.Load().(*zap.Logger).Sugar()
_globalS.Store(s)
r := utils.NewRateLimiter(1.0, 60.0)
_globalR.Store(r)
updateLoglLevel := func(w http.ResponseWriter, req *http.Request) {
_globalP.Load().(*ZapProperties).Level.ServeHTTP(w, req)
}
http.HandleFunc("/log/level", updateLoglLevel)
}
// InitLogger initializes a zap logger.
func InitLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
var output zapcore.WriteSyncer
if len(cfg.File.Filename) > 0 {
lg, err := initFileLog(&cfg.File)
if err != nil {
return nil, nil, err
}
output = zapcore.AddSync(lg)
} else {
stdOut, _, err := zap.Open([]string{"stdout"}...)
if err != nil {
return nil, nil, err
}
output = stdOut
}
debugCfg := *cfg
debugCfg.Level = "debug"
debugL, r, err := InitLoggerWithWriteSyncer(&debugCfg, output, opts...)
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
}
// InitTestLogger initializes a logger for unit tests
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...)
}
// 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 {
return nil, nil, fmt.Errorf("initLoggerWithWriteSyncer UnmarshalText cfg.Level err:%w", err)
}
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) {
if st, err := os.Stat(cfg.Filename); err == nil {
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{
Filename: cfg.Filename,
MaxSize: cfg.MaxSize,
MaxBackups: cfg.MaxBackups,
MaxAge: cfg.MaxDays,
LocalTime: true,
}, nil
}
func newStdLogger() (*zap.Logger, *ZapProperties) {
conf := &Config{Level: "debug", File: FileLogConfig{}}
lg, r, _ := InitLogger(conf)
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)
}
// R returns utils.ReconfigurableRateLimiter.
func R() *utils.ReconfigurableRateLimiter {
return _globalR.Load().(*utils.ReconfigurableRateLimiter)
}
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)
}
// 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)
}
func replaceLeveledLoggers(debugLogger *zap.Logger) {
levels := []zapcore.Level{zapcore.DebugLevel, zapcore.InfoLevel, zapcore.WarnLevel, zapcore.ErrorLevel,
zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel}
for _, level := range levels {
levelL := debugLogger.WithOptions(zap.IncreaseLevel(level))
_globalLevelLogger.Store(level, levelL)
}
}
// Sync flushes any buffered log entries.
func Sync() error {
if err := L().Sync(); err != nil {
return err
}
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
}