status-go/logutils/zap_adapter.go
2023-11-09 14:35:59 +08:00

153 lines
4 KiB
Go

package logutils
import (
"fmt"
"math"
"sync"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/protocol/zaputil"
)
type gethLoggerCore struct {
zapcore.LevelEnabler
fields []zapcore.Field
logger log.Logger
}
func (c gethLoggerCore) With(fields []zapcore.Field) zapcore.Core {
clone := c.clone()
clone.fields = append(clone.fields, fields...)
return clone
}
func (c gethLoggerCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.Enabled(ent.Level) {
return ce.AddCore(ent, c)
}
return ce
}
func (c gethLoggerCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
fields = append(c.fields[:], fields...)
var args []interface{}
for _, f := range fields {
switch f.Type {
case zapcore.ArrayMarshalerType,
zapcore.ObjectMarshalerType,
zapcore.BinaryType,
zapcore.ByteStringType,
zapcore.Complex128Type,
zapcore.ReflectType,
zapcore.StringerType,
zapcore.ErrorType:
args = append(args, f.Key, f.Interface)
case zapcore.BoolType:
args = append(args, f.Key, f.Integer == 1)
case zapcore.DurationType:
args = append(args, f.Key, time.Duration(f.Integer))
case zapcore.Float64Type:
args = append(args, f.Key, math.Float64frombits(uint64(f.Integer)))
case zapcore.Float32Type:
args = append(args, f.Key, math.Float32frombits(uint32(f.Integer)))
case zapcore.Int64Type,
zapcore.Int32Type,
zapcore.Int16Type,
zapcore.Int8Type,
zapcore.Uint64Type,
zapcore.Uint32Type,
zapcore.Uint16Type,
zapcore.Uint8Type:
args = append(args, f.Key, f.Integer)
case zapcore.UintptrType:
args = append(args, f.Key, uintptr(f.Integer))
case zapcore.StringType:
args = append(args, f.Key, f.String)
case zapcore.TimeType:
if f.Interface != nil {
args = append(args, f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
} else {
// Fall back to UTC if location is nil.
args = append(args, f.Key, time.Unix(0, f.Integer))
}
case zapcore.NamespaceType:
args = append(args, "namespace", f.Key)
case zapcore.SkipType:
break
default:
panic(fmt.Sprintf("unknown field type: %v", f))
}
}
// set callDepth to 3 for `Output` to skip the calls to zap.Logger
// and get the correct caller in the log
callDepth := 3
switch ent.Level {
case zapcore.DebugLevel:
c.logger.Output(ent.Message, log.LvlDebug, callDepth, args...)
case zapcore.InfoLevel:
c.logger.Output(ent.Message, log.LvlInfo, callDepth, args...)
case zapcore.WarnLevel:
c.logger.Output(ent.Message, log.LvlWarn, callDepth, args...)
case zapcore.ErrorLevel:
c.logger.Output(ent.Message, log.LvlError, callDepth, args...)
case zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel:
c.logger.Output(ent.Message, log.LvlCrit, callDepth, args...)
}
return nil
}
func (gethLoggerCore) Sync() error {
return nil
}
func (c *gethLoggerCore) clone() *gethLoggerCore {
return &gethLoggerCore{
LevelEnabler: c.LevelEnabler,
fields: c.fields,
logger: c.logger,
}
}
// NewZapAdapter returns a new zapcore.Core interface which forwards logs to log.Logger.
func NewZapAdapter(logger log.Logger, enab zapcore.LevelEnabler) zapcore.Core {
return &gethLoggerCore{
LevelEnabler: enab,
logger: logger,
}
}
var registerOnce sync.Once
// NewZapLoggerWithAdapter returns a logger forwarding all logs with level info and above.
func NewZapLoggerWithAdapter(logger log.Logger) (*zap.Logger, error) {
registerOnce.Do(func() {
if err := zaputil.RegisterJSONHexEncoder(); err != nil {
panic(err)
}
})
cfg := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel),
Development: false,
Sampling: nil,
Encoding: "json-hex",
EncoderConfig: zap.NewProductionEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
adapter := zap.WrapCore(
func(zapcore.Core) zapcore.Core {
return NewZapAdapter(logger, cfg.Level)
},
)
log.PrintOrigins(true)
return cfg.Build(adapter)
}