package log import ( "OpenIM/pkg/common/constant" "OpenIM/pkg/common/tracelog" "context" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( pkgLogger Logger = &ZapLogger{} ) // InitFromConfig initializes a Zap-based logger func InitFromConfig(name string) error { l, err := NewZapLogger() if err != nil { return err } pkgLogger = l.WithCallDepth(2).WithName(name) return nil } 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...) } 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() (*ZapLogger, error) { zapConfig := zap.Config{ Level: zap.NewAtomicLevelAt(zapcore.DebugLevel), Development: true, Encoding: "json", EncoderConfig: zap.NewProductionEncoderConfig(), OutputPaths: []string{"stdout"}, Sampling: &zap.SamplingConfig{ Initial: 0, Thereafter: 0, Hook: nil, }, } l, err := zapConfig.Build() if err != nil { return nil, err } zl := &ZapLogger{ unsampled: l.Sugar(), } 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...) 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 }