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