func NewCollector(db *shed.DB) (*Collector, error) {
	const name = "kademlia-counters"
	c := new(Collector)
	val, err := db.NewStructField(name)
	if err != nil {
		return nil, fmt.Errorf("field initialization for %q failed: %w", name, err)
	}
	c.persistence = &val
	counters := make(map[string]persistentCounters)
	if err := val.Get(&counters); err != nil && !errors.Is(err, leveldb.ErrNotFound) {
		return nil, err
	}
	for _, val := range counters {
		c.counters.Store(val.PeerAddress.ByteString(), &Counters{
			peerAddress:       val.PeerAddress,
			lastSeenTimestamp: val.LastSeenTimestamp,
			connTotalDuration: val.ConnTotalDuration,
		})
	}
	return c, nil
}
NewCollector is a convenient constructor for creating new Collector.
package metrics
import (
	"encoding/json"
	"errors"
	"fmt"
	"sync"
	"time"
	"github.com/ethersphere/bee/pkg/shed"
	"github.com/ethersphere/bee/pkg/swarm"
	"github.com/syndtr/goleveldb/leveldb"
)
type PeerConnectionDirection string
const (
	PeerConnectionDirectionInbound  PeerConnectionDirection = "inbound"
	PeerConnectionDirectionOutbound PeerConnectionDirection = "outbound"
	ewmaSmoothing = 0.1
)
type RecordOp func(*Counters)
func PeerLogIn(t time.Time, dir PeerConnectionDirection) RecordOp {
	return func(cs *Counters) {
		cs.Lock()
		defer cs.Unlock()
		if cs.isLoggedIn {
			return  
		}
		cs.isLoggedIn = true
		ls := t.UnixNano()
		if ls < 0 {
			panic(fmt.Errorf("time before unix epoch: %s", t))
		}
		cs.sessionConnDirection = dir
		cs.lastSeenTimestamp = ls
	}
}
func PeerLogOut(t time.Time) RecordOp {
	return func(cs *Counters) {
		cs.Lock()
		defer cs.Unlock()
		if !cs.isLoggedIn {
			return  
		}
		cs.isLoggedIn = false
		curLs := cs.lastSeenTimestamp
		newLs := t.UnixNano()
		if newLs < 0 {
			panic(fmt.Errorf("time before unix epoch: %s", t))
		}
		cs.sessionConnDuration = time.Duration(newLs - curLs)
		cs.connTotalDuration += cs.sessionConnDuration
		cs.lastSeenTimestamp = newLs
	}
}
func IncSessionConnectionRetry() RecordOp {
	return func(cs *Counters) {
		cs.Lock()
		defer cs.Unlock()
		cs.sessionConnRetry++
	}
}
func PeerLatency(t time.Duration) RecordOp {
	return func(cs *Counters) {
		cs.Lock()
		defer cs.Unlock()
		if cs.latencyEWMA == 0 {
			cs.latencyEWMA = t
			return
		}
		v := (ewmaSmoothing * float64(t)) + (1-ewmaSmoothing)*float64(cs.latencyEWMA)
		cs.latencyEWMA = time.Duration(v)
	}
}
type Snapshot struct {
	LastSeenTimestamp          int64
	SessionConnectionRetry     uint64
	ConnectionTotalDuration    time.Duration
	SessionConnectionDuration  time.Duration
	SessionConnectionDirection PeerConnectionDirection
	LatencyEWMA                time.Duration
}
func (ss *Snapshot) HasAtMaxOneConnectionAttempt() bool {
	return ss.LastSeenTimestamp == 0 && ss.SessionConnectionRetry <= 1
}
type persistentCounters struct {
	PeerAddress       swarm.Address `json:"peerAddress"`
	LastSeenTimestamp int64         `json:"lastSeenTimestamp"`
	ConnTotalDuration time.Duration `json:"connTotalDuration"`
}
type Counters struct {
	sync.Mutex
	isLoggedIn  bool
	peerAddress swarm.Address
	lastSeenTimestamp    int64
	connTotalDuration    time.Duration
	sessionConnRetry     uint64
	sessionConnDuration  time.Duration
	sessionConnDirection PeerConnectionDirection
	latencyEWMA time.Duration
}
func (cs *Counters) UnmarshalJSON(b []byte) (err error) {
	var val persistentCounters
	if err := json.Unmarshal(b, &val); err != nil {
		return err
	}
	cs.Lock()
	cs.peerAddress = val.PeerAddress
	cs.lastSeenTimestamp = val.LastSeenTimestamp
	cs.connTotalDuration = val.ConnTotalDuration
	cs.Unlock()
	return nil
}
func (cs *Counters) MarshalJSON() ([]byte, error) {
	cs.Lock()
	val := persistentCounters{
		PeerAddress:       cs.peerAddress,
		LastSeenTimestamp: cs.lastSeenTimestamp,
		ConnTotalDuration: cs.connTotalDuration,
	}
	cs.Unlock()
	return json.Marshal(val)
}
func (cs *Counters) snapshot(t time.Time) *Snapshot {
	cs.Lock()
	defer cs.Unlock()
	connTotalDuration := cs.connTotalDuration
	sessionConnDuration := cs.sessionConnDuration
	if cs.isLoggedIn {
		sessionConnDuration = t.Sub(time.Unix(0, cs.lastSeenTimestamp))
		connTotalDuration += sessionConnDuration
	}
	return &Snapshot{
		LastSeenTimestamp:          cs.lastSeenTimestamp,
		SessionConnectionRetry:     cs.sessionConnRetry,
		ConnectionTotalDuration:    connTotalDuration,
		SessionConnectionDuration:  sessionConnDuration,
		SessionConnectionDirection: cs.sessionConnDirection,
		LatencyEWMA:                cs.latencyEWMA,
	}
}