parent
d545a7f753
commit
662254f30f
@ -0,0 +1,23 @@
|
||||
package log
|
||||
|
||||
type Config struct {
|
||||
JSON bool `yaml:"json"`
|
||||
Level string `yaml:"level"`
|
||||
// true to enable log sampling, where the same log message and level will be throttled.
|
||||
// we have two layers of sampling
|
||||
// 1. global sampling - within a second, it will log the first SampleInitial, then every SampleInterval messages.
|
||||
// 2. per participant/track sampling - to be used with Logger.WithItemSampler(). This would be used to throttle
|
||||
// the logs for a particular participant/track.
|
||||
Sample bool `yaml:"sample,omitempty"`
|
||||
|
||||
// global sampling per server
|
||||
// when sampling, the first N logs will be logged
|
||||
SampleInitial int `yaml:"sample_initial,omitempty"`
|
||||
// when sampling, every Mth log will be logged
|
||||
SampleInterval int `yaml:"sample_interval,omitempty"`
|
||||
|
||||
// participant/track level sampling
|
||||
ItemSampleSeconds int `yaml:"item_sample_seconds,omitempty"`
|
||||
ItemSampleInitial int `yaml:"item_sample_initial,omitempty"`
|
||||
ItemSampleInterval int `yaml:"item_sample_interval,omitempty"`
|
||||
}
|
@ -0,0 +1,277 @@
|
||||
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
|
||||
}
|
Loading…
Reference in new issue