2023-03-10 18:37:36 +08:00

278 lines
7.7 KiB
Go

package log
import (
"OpenIM/pkg/common/constant"
"OpenIM/pkg/common/tracelog"
"context"
"time"
"github.com/go-logr/logr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
discardLogger = logr.Discard()
defaultLogger Logger = LogRLogger(discardLogger)
pkgLogger Logger = LogRLogger(discardLogger)
)
// InitFromConfig initializes a Zap-based logger
func InitFromConfig(conf Config, name string) {
l, err := NewZapLogger(&conf)
if err == nil {
setLogger(l, name)
}
}
// GetLogger returns the logger that was set with SetLogger with an extra depth of 1
func GetLogger() Logger {
return defaultLogger
}
// SetLogger lets you use a custom logger. Pass in a logr.Logger with default depth
func setLogger(l Logger, name string) {
defaultLogger = l.WithCallDepth(1).WithName(name)
// pkg wrapper needs to drop two levels of depth
pkgLogger = l.WithCallDepth(2).WithName(name)
}
func Debug(ctx context.Context, msg string, keysAndValues ...interface{}) {
pkgLogger.Debug(ctx, msg, keysAndValues...)
}
func Info(ctx context.Context, msg string, keysAndValues ...interface{}) {
pkgLogger.Info(ctx, msg, keysAndValues...)
}
func Warn(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
pkgLogger.Warn(ctx, msg, err, keysAndValues...)
}
func Error(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
pkgLogger.Error(ctx, msg, err, keysAndValues...)
}
func ParseZapLevel(level string) zapcore.Level {
lvl := zapcore.InfoLevel
if level != "" {
_ = lvl.UnmarshalText([]byte(level))
}
return lvl
}
type Logger interface {
Debug(ctx context.Context, msg string, keysAndValues ...interface{})
Info(ctx context.Context, msg string, keysAndValues ...interface{})
Warn(ctx context.Context, msg string, err error, keysAndValues ...interface{})
Error(ctx context.Context, msg string, err error, keysAndValues ...interface{})
WithValues(keysAndValues ...interface{}) Logger
WithName(name string) Logger
WithCallDepth(depth int) Logger
WithItemSampler() Logger
// WithoutSampler returns the original logger without sampling
WithoutSampler() Logger
}
type ZapLogger struct {
zap *zap.SugaredLogger
// store original logger without sampling to avoid multiple samplers
unsampled *zap.SugaredLogger
SampleDuration time.Duration
SampleInitial int
SampleInterval int
}
func NewZapLogger(conf *Config) (*ZapLogger, error) {
lvl := ParseZapLevel(conf.Level)
zapConfig := zap.Config{
Level: zap.NewAtomicLevelAt(lvl),
Development: false,
Encoding: "console",
EncoderConfig: zap.NewDevelopmentEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
if conf.JSON {
zapConfig.Encoding = "json"
zapConfig.EncoderConfig = zap.NewProductionEncoderConfig()
}
l, err := zapConfig.Build()
if err != nil {
return nil, err
}
zl := &ZapLogger{
unsampled: l.Sugar(),
SampleDuration: time.Duration(conf.ItemSampleSeconds) * time.Second,
SampleInitial: conf.ItemSampleInitial,
SampleInterval: conf.ItemSampleInterval,
}
if conf.Sample {
// use a sampling logger for the main logger
samplingConf := &zap.SamplingConfig{
Initial: conf.SampleInitial,
Thereafter: conf.SampleInterval,
}
// sane defaults
if samplingConf.Initial == 0 {
samplingConf.Initial = 20
}
if samplingConf.Thereafter == 0 {
samplingConf.Thereafter = 100
}
zl.zap = l.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSamplerWithOptions(
core,
time.Second,
samplingConf.Initial,
samplingConf.Thereafter,
)
})).Sugar()
} else {
zl.zap = zl.unsampled
}
return zl, nil
}
func (l *ZapLogger) ToZap() *zap.SugaredLogger {
return l.zap
}
func (l *ZapLogger) Debug(ctx context.Context, msg string, keysAndValues ...interface{}) {
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.zap.Debugw(msg, keysAndValues...)
}
func (l *ZapLogger) Info(ctx context.Context, msg string, keysAndValues ...interface{}) {
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.zap.Infow(msg, keysAndValues...)
}
func (l *ZapLogger) Warn(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
if err != nil {
keysAndValues = append(keysAndValues, "error", err)
}
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.zap.Warnw(msg, keysAndValues...)
}
func (l *ZapLogger) Error(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
if err != nil {
keysAndValues = append(keysAndValues, "error", err)
}
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.zap.Errorw(msg, keysAndValues...)
}
func (l *ZapLogger) WithValues(keysAndValues ...interface{}) Logger {
dup := *l
dup.zap = l.zap.With(keysAndValues...)
// mirror unsampled logger too
if l.unsampled == l.zap {
dup.unsampled = dup.zap
} else {
dup.unsampled = l.unsampled.With(keysAndValues...)
}
return &dup
}
func (l *ZapLogger) WithName(name string) Logger {
dup := *l
dup.zap = l.zap.Named(name)
if l.unsampled == l.zap {
dup.unsampled = dup.zap
} else {
dup.unsampled = l.unsampled.Named(name)
}
return &dup
}
func (l *ZapLogger) WithCallDepth(depth int) Logger {
dup := *l
dup.zap = l.zap.WithOptions(zap.AddCallerSkip(depth))
if l.unsampled == l.zap {
dup.unsampled = dup.zap
} else {
dup.unsampled = l.unsampled.WithOptions(zap.AddCallerSkip(depth))
}
return &dup
}
func (l *ZapLogger) WithItemSampler() Logger {
if l.SampleDuration == 0 {
return l
}
dup := *l
dup.zap = l.unsampled.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSamplerWithOptions(
core,
l.SampleDuration,
l.SampleInitial,
l.SampleInterval,
)
}))
return &dup
}
func (l *ZapLogger) WithoutSampler() Logger {
if l.SampleDuration == 0 {
return l
}
dup := *l
dup.zap = l.unsampled
return &dup
}
type LogRLogger logr.Logger
func (l LogRLogger) toLogr() logr.Logger {
if logr.Logger(l).GetSink() == nil {
return discardLogger
}
return logr.Logger(l)
}
func (l LogRLogger) Debug(ctx context.Context, msg string, keysAndValues ...interface{}) {
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.toLogr().V(1).Info(msg, keysAndValues...)
}
func (l LogRLogger) Info(ctx context.Context, msg string, keysAndValues ...interface{}) {
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.toLogr().Info(msg, keysAndValues...)
}
func (l LogRLogger) Warn(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
if err != nil {
keysAndValues = append(keysAndValues, "error", err)
}
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.toLogr().Info(msg, keysAndValues...)
}
func (l LogRLogger) Error(ctx context.Context, msg string, err error, keysAndValues ...interface{}) {
keysAndValues = append([]interface{}{constant.OperationID, tracelog.GetOperationID(ctx)}, keysAndValues...)
l.toLogr().Error(err, msg, keysAndValues...)
}
func (l LogRLogger) WithValues(keysAndValues ...interface{}) Logger {
return LogRLogger(l.toLogr().WithValues(keysAndValues...))
}
func (l LogRLogger) WithName(name string) Logger {
return LogRLogger(l.toLogr().WithName(name))
}
func (l LogRLogger) WithCallDepth(depth int) Logger {
return LogRLogger(l.toLogr().WithCallDepth(depth))
}
func (l LogRLogger) WithItemSampler() Logger {
return l
}
func (l LogRLogger) WithoutSampler() Logger {
return l
}