logging/logging.go
// logging implements custom logging field types for commonly
// logged values like host ID or wallet address.
//
// implementation purposely does as little as possible at field creation time,
// and postpones any transformation to output time by relying on the generic
// zap types like zap.Stringer, zap.Array, zap.Object
package logging
import (
"encoding/hex"
"fmt"
"net"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
"github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb"
wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// List of []byte
type byteArr [][]byte
// HexArray creates a field with an array of bytes that will be shown as a hexadecimal string in logs
func HexArray(key string, byteVal byteArr) zapcore.Field {
return zap.Array(key, byteVal)
}
func (bArr byteArr) MarshalLogArray(encoder zapcore.ArrayEncoder) error {
for _, b := range bArr {
encoder.AppendString("0x" + hex.EncodeToString(b))
}
return nil
}
// List of multiaddrs
type multiaddrs []multiaddr.Multiaddr
// MultiAddrs creates a field with an array of multiaddrs
func MultiAddrs(key string, addrs ...multiaddr.Multiaddr) zapcore.Field {
return zap.Array(key, multiaddrs(addrs))
}
func (addrs multiaddrs) MarshalLogArray(encoder zapcore.ArrayEncoder) error {
for _, addr := range addrs {
encoder.AppendString(addr.String())
}
return nil
}
// Host ID/Peer ID
type hostID peer.ID
// HostID creates a field for a peer.ID
func HostID(key string, id peer.ID) zapcore.Field {
return zap.Stringer(key, hostID(id))
}
func (id hostID) String() string { return peer.ID(id).String() }
// Time - Waku uses Nanosecond Unix Time
type timestamp int64
// Time creates a field for Waku time value
func Time(key string, time int64) zapcore.Field {
return zap.Stringer(key, timestamp(time))
}
func (t timestamp) String() string {
return time.Unix(0, int64(t)).Format(time.RFC3339)
}
func Timep(key string, time *int64) zapcore.Field {
if time == nil {
return zap.String(key, "-")
} else {
return Time(key, *time)
}
}
func Epoch(key string, time time.Time) zap.Field {
return zap.String(key, fmt.Sprintf("%d", time.UnixNano()))
}
// History Query Filters
type historyFilters []*pb.ContentFilter
// Filters creates a field with an array of history query filters.
// The assumption is that log entries won't have more than one of these,
// so the field key/name is hardcoded to be "filters" to promote consistency.
func Filters(filters []*pb.ContentFilter) zapcore.Field {
return zap.Array("filters", historyFilters(filters))
}
func (filters historyFilters) MarshalLogArray(encoder zapcore.ArrayEncoder) error {
for _, filter := range filters {
encoder.AppendString(filter.ContentTopic)
}
return nil
}
// History Paging Info
// Probably too detailed for normal log levels, but useful for debugging.
// Also a good example of nested object value.
type pagingInfo pb.PagingInfo
type index pb.Index
// PagingInfo creates a field with history query paging info.
func PagingInfo(pi *pb.PagingInfo) zapcore.Field {
return zap.Object("paging_info", (*pagingInfo)(pi))
}
func (pi *pagingInfo) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddUint64("page_size", pi.PageSize)
encoder.AddString("direction", pi.Direction.String())
if pi.Cursor != nil {
return encoder.AddObject("cursor", (*index)(pi.Cursor))
}
return nil
}
func (i *index) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddBinary("digest", i.Digest)
encoder.AddTime("sent", time.Unix(0, i.SenderTime))
encoder.AddTime("received", time.Unix(0, i.ReceiverTime))
return nil
}
// Hex encoded bytes
type hexBytes []byte
// HexBytes creates a field for a byte slice that should be emitted as hex encoded string.
func HexBytes(key string, bytes []byte) zap.Field {
return zap.Stringer(key, hexBytes(bytes))
}
func (bytes hexBytes) String() string {
return hexutil.Encode(bytes)
}
func Hash(hash wpb.MessageHash) zap.Field {
return zap.Stringer("hash", hash)
}
// ENode creates a field for ENR node.
func ENode(key string, node *enode.Node) zap.Field {
return zap.Stringer(key, node)
}
// TCPAddr creates a field for TCP v4/v6 address and port
func TCPAddr(key string, ip net.IP, port int) zap.Field {
return zap.Stringer(key, &net.TCPAddr{IP: ip, Port: port})
}
// UDPAddr creates a field for UDP v4/v6 address and port
func UDPAddr(key string, ip net.IP, port int) zap.Field {
return zap.Stringer(key, &net.UDPAddr{IP: ip, Port: port})
}
func Uint64(key string, value uint64) zap.Field {
valueStr := fmt.Sprintf("%v", value)
return zap.String(key, valueStr)
}
func UTCTime(key string, t time.Time) zap.Field {
return zap.Time(key, t.UTC())
}