status-im/status-go

View on GitHub
logutils/zap_adapter.go

Summary

Maintainability
A
0 mins
Test Coverage
B
80%
package logutils

import (
    "fmt"
    "math"
    "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,
    }
}

// NewZapLoggerWithAdapter returns a logger forwarding all logs with level info and above.
func NewZapLoggerWithAdapter(logger log.Logger) (*zap.Logger, error) {
    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)
}