mirror of
https://github.com/hanzozt/dilithium.git
synced 2026-10-30 20:38:17 +00:00
491 lines
15 KiB
Go
491 lines
15 KiB
Go
package westworld3
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/openziti-incubator/cf"
|
|
"github.com/hanzozt/dilithium/util"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var localEnabled = false
|
|
var localEnabledOverridden = false
|
|
|
|
type metricsInstrument struct {
|
|
lock *sync.Mutex
|
|
config *metricsInstrumentConfig
|
|
instances []*metricsInstrumentInstance
|
|
}
|
|
|
|
type metricsInstrumentConfig struct {
|
|
Path string `cf:"path"`
|
|
SnapshotMs int `cf:"snapshot_ms"`
|
|
Enabled bool `cf:"enabled"`
|
|
}
|
|
|
|
func NewMetricsInstrument(config map[string]interface{}) (Instrument, error) {
|
|
i := &metricsInstrument{
|
|
lock: new(sync.Mutex),
|
|
config: &metricsInstrumentConfig{
|
|
SnapshotMs: 1000,
|
|
},
|
|
}
|
|
if err := cf.Bind(i.config, config, cf.DefaultOptions()); err != nil {
|
|
return nil, errors.Wrap(err, "unable to load config")
|
|
}
|
|
if localEnabledOverridden {
|
|
i.config.Enabled = localEnabled
|
|
}
|
|
cl, err := util.GetCtrlListener(i.config.Path, "westworld3")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "unable to get metrics ctrl listener")
|
|
}
|
|
cl.AddCallback("start", func(string, net.Conn) (int64, error) {
|
|
localEnabled = true
|
|
localEnabledOverridden = true
|
|
|
|
i.config.Enabled = true
|
|
return 0, nil
|
|
})
|
|
cl.AddCallback("stop", func(string, net.Conn) (int64, error) {
|
|
localEnabled = false
|
|
localEnabledOverridden = true
|
|
|
|
i.config.Enabled = false
|
|
return 0, nil
|
|
})
|
|
cl.AddCallback("write", func(string, net.Conn) (int64, error) {
|
|
err := i.writeAllSamples()
|
|
if err != nil {
|
|
logrus.Errorf("error writing samples (%v)", err)
|
|
}
|
|
return 0, err
|
|
})
|
|
cl.AddCallback("clean", func(string, net.Conn) (int64, error) {
|
|
i.clean()
|
|
return 0, nil
|
|
})
|
|
cl.Start()
|
|
logrus.Info(cf.Dump(i.config, cf.DefaultOptions()))
|
|
return i, nil
|
|
}
|
|
|
|
func (self *metricsInstrument) NewInstance(id string, peer *net.UDPAddr) InstrumentInstance {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
ii := &metricsInstrumentInstance{id: id, peer: peer, config: self.config, close: make(chan struct{}, 1)}
|
|
go ii.snapshotter(self.config.SnapshotMs)
|
|
self.instances = append(self.instances, ii)
|
|
return ii
|
|
}
|
|
|
|
func (self *metricsInstrument) writeAllSamples() error {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
|
|
for _, ii := range self.instances {
|
|
peerName := strings.ReplaceAll(fmt.Sprintf("%s_", ii.id), ":", "-")
|
|
if err := os.MkdirAll(self.config.Path, os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
outPath, err := ioutil.TempDir(self.config.Path, peerName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logrus.Infof("writing metrics to: %s", outPath)
|
|
|
|
var values map[string]string
|
|
if ii.listenerAddr != nil {
|
|
values = make(map[string]string)
|
|
values["listener"] = ii.listenerAddr.String()
|
|
}
|
|
if err := util.WriteMetricsId(fmt.Sprintf("westworld3.%d", protocolVersion), outPath, values); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_bytes", outPath, ii.txBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_msgs", outPath, ii.txMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("retx_bytes", outPath, ii.retxBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("retx_msgs", outPath, ii.retxMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_bytes", outPath, ii.rxBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_msgs", outPath, ii.rxMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_ack_bytes", outPath, ii.txAckBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_ack_msgs", outPath, ii.txAckMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_ack_bytes", outPath, ii.rxAckBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_ack_msgs", outPath, ii.rxAckMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_keepalive_bytes", outPath, ii.txKeepaliveBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_keepalive_msgs", outPath, ii.txKeepaliveMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_keepalive_bytes", outPath, ii.rxKeepaliveBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_keepalive_msgs", outPath, ii.rxKeepaliveMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_portal_capacity", outPath, ii.txPortalCapacity); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_portal_sz", outPath, ii.txPortalSz); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("tx_portal_rx_sz", outPath, ii.txPortalRxSz); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("retx_ms", outPath, ii.retxMs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("retx_scale", outPath, ii.retxScale); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("dup_acks", outPath, ii.dupAcks); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("rx_portal_sz", outPath, ii.rxPortalSz); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("dup_rx_bytes", outPath, ii.dupRxBytes); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("dup_rx_msgs", outPath, ii.dupRxMsgs); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("allocations", outPath, ii.allocations); err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteSamples("errors", outPath, ii.errors); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *metricsInstrument) clean() {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
|
|
idx := self.findClosed()
|
|
for idx != -1 {
|
|
logrus.Infof("removed metricsInstrumentInstance #%p", self.instances[idx])
|
|
self.instances = append(self.instances[:idx], self.instances[idx+1:]...)
|
|
idx = self.findClosed()
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrument) findClosed() int {
|
|
for i, ii := range self.instances {
|
|
if ii.closed {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
type metricsInstrumentInstance struct {
|
|
id string
|
|
peer *net.UDPAddr
|
|
listenerAddr *net.UDPAddr
|
|
config *metricsInstrumentConfig
|
|
close chan struct{}
|
|
closed bool
|
|
|
|
txBytes []*util.Sample
|
|
txBytesAccum int64
|
|
txMsgs []*util.Sample
|
|
txMsgsAccum int64
|
|
retxBytes []*util.Sample
|
|
retxBytesAccum int64
|
|
retxMsgs []*util.Sample
|
|
retxMsgsAccum int64
|
|
rxBytes []*util.Sample
|
|
rxBytesAccum int64
|
|
rxMsgs []*util.Sample
|
|
rxMsgsAccum int64
|
|
|
|
txAckBytes []*util.Sample
|
|
txAckBytesAccum int64
|
|
txAckMsgs []*util.Sample
|
|
txAckMsgsAccum int64
|
|
rxAckBytes []*util.Sample
|
|
rxAckBytesAccum int64
|
|
rxAckMsgs []*util.Sample
|
|
rxAckMsgsAccum int64
|
|
txKeepaliveBytes []*util.Sample
|
|
txKeepaliveBytesAccum int64
|
|
txKeepaliveMsgs []*util.Sample
|
|
txKeepaliveMsgsAccum int64
|
|
rxKeepaliveBytes []*util.Sample
|
|
rxKeepaliveBytesAccum int64
|
|
rxKeepaliveMsgs []*util.Sample
|
|
rxKeepaliveMsgsAccum int64
|
|
|
|
txPortalCapacity []*util.Sample
|
|
txPortalCapacityVal int64
|
|
txPortalSz []*util.Sample
|
|
txPortalSzVal int64
|
|
txPortalRxSz []*util.Sample
|
|
txPortalRxSzVal int64
|
|
retxMs []*util.Sample
|
|
retxMsVal int64
|
|
retxScale []*util.Sample
|
|
retxScaleVal int64
|
|
dupAcks []*util.Sample
|
|
dupAcksAccum int64
|
|
|
|
rxPortalSz []*util.Sample
|
|
rxPortalSzVal int64
|
|
dupRxBytes []*util.Sample
|
|
dupRxBytesAccum int64
|
|
dupRxMsgs []*util.Sample
|
|
dupRxMsgsAccum int64
|
|
|
|
allocations []*util.Sample
|
|
allocationsAccum int64
|
|
errors []*util.Sample
|
|
errorsAccum int64
|
|
}
|
|
|
|
/*
|
|
* connection
|
|
*/
|
|
func (self *metricsInstrumentInstance) Listener(addr *net.UDPAddr) {
|
|
self.listenerAddr = addr
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) Hello(*net.UDPAddr) {}
|
|
func (self *metricsInstrumentInstance) Connected(*net.UDPAddr) {}
|
|
func (self *metricsInstrumentInstance) ConnectionError(*net.UDPAddr, error) {}
|
|
|
|
func (self *metricsInstrumentInstance) Closed(*net.UDPAddr) {
|
|
logrus.Infof("closing snapshotter")
|
|
if !self.closed {
|
|
self.closed = true
|
|
close(self.close)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* wire
|
|
*/
|
|
func (self *metricsInstrumentInstance) WireMessageTx(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.txBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.txMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) WireMessageRetx(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.retxBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.retxMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) WireMessageRx(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.rxBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.rxMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) UnknownPeer(peer *net.UDPAddr) {
|
|
if self.config.Enabled {
|
|
logrus.Errorf("unknown peer (%v)", peer)
|
|
atomic.AddInt64(&self.errorsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) ReadError(_ *net.UDPAddr, err error) {
|
|
if self.config.Enabled {
|
|
logrus.Errorf("read error (%v)", err)
|
|
atomic.AddInt64(&self.errorsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) UnexpectedMessageType(_ *net.UDPAddr, mt messageType) {
|
|
if self.config.Enabled {
|
|
logrus.Errorf("unexpected message type (%d)", mt)
|
|
atomic.AddInt64(&self.errorsAccum, 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* control
|
|
*/
|
|
func (self *metricsInstrumentInstance) TxAck(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.txAckBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.txAckMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) RxAck(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.rxAckBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.rxAckMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) TxKeepalive(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.txKeepaliveBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.txKeepaliveMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) RxKeepalive(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.rxKeepaliveBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.rxKeepaliveMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* txPortal
|
|
*/
|
|
func (self *metricsInstrumentInstance) TxPortalCapacityChanged(_ *net.UDPAddr, capacity int) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.txPortalCapacityVal, int64(capacity))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) TxPortalSzChanged(_ *net.UDPAddr, sz int) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.txPortalSzVal, int64(sz))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) TxPortalRxSzChanged(_ *net.UDPAddr, sz int) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.txPortalRxSzVal, int64(sz))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) NewRetxMs(_ *net.UDPAddr, ms int) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.retxMsVal, int64(ms))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) NewRetxScale(_ *net.UDPAddr, retxMs float64) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.retxScaleVal, int64(retxMs*1000.0))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) DuplicateAck(*net.UDPAddr, int32) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.dupAcksAccum, 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rxPortal
|
|
*/
|
|
func (self *metricsInstrumentInstance) RxPortalSzChanged(_ *net.UDPAddr, sz int) {
|
|
if self.config.Enabled {
|
|
atomic.StoreInt64(&self.rxPortalSzVal, int64(sz))
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) DuplicateRx(_ *net.UDPAddr, wm *wireMessage) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.dupRxBytesAccum, int64(wm.buffer.uz))
|
|
atomic.AddInt64(&self.dupRxMsgsAccum, 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* allocation
|
|
*/
|
|
func (self *metricsInstrumentInstance) Allocate(string) {
|
|
if self.config.Enabled {
|
|
atomic.AddInt64(&self.allocationsAccum, 1)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* instrument lifecycle
|
|
*/
|
|
func (self *metricsInstrumentInstance) Shutdown() {
|
|
if !self.closed {
|
|
self.closed = true
|
|
close(self.close)
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) snapshotter(ms int) {
|
|
logrus.Infof("started")
|
|
defer logrus.Infof("exited")
|
|
for {
|
|
time.Sleep(time.Duration(ms) * time.Millisecond)
|
|
if self.config.Enabled {
|
|
self.snapshot()
|
|
}
|
|
select {
|
|
case <-self.close:
|
|
self.snapshot()
|
|
return
|
|
default:
|
|
//
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *metricsInstrumentInstance) snapshot() {
|
|
now := time.Now()
|
|
self.txBytes = append(self.txBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txBytesAccum, 0)})
|
|
self.txMsgs = append(self.txMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txMsgsAccum, 0)})
|
|
self.retxBytes = append(self.retxBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.retxBytesAccum, 0)})
|
|
self.retxMsgs = append(self.retxMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.retxMsgsAccum, 0)})
|
|
self.rxBytes = append(self.rxBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxBytesAccum, 0)})
|
|
self.rxMsgs = append(self.rxMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxMsgsAccum, 0)})
|
|
self.txAckBytes = append(self.txAckBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txAckBytesAccum, 0)})
|
|
self.txAckMsgs = append(self.txAckMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txAckMsgsAccum, 0)})
|
|
self.rxAckBytes = append(self.rxAckBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxAckBytesAccum, 0)})
|
|
self.rxAckMsgs = append(self.rxAckMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxAckMsgsAccum, 0)})
|
|
self.txKeepaliveBytes = append(self.txKeepaliveBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txKeepaliveBytesAccum, 0)})
|
|
self.txKeepaliveMsgs = append(self.txKeepaliveMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.txKeepaliveMsgsAccum, 0)})
|
|
self.rxKeepaliveBytes = append(self.rxKeepaliveBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxKeepaliveBytesAccum, 0)})
|
|
self.rxKeepaliveMsgs = append(self.rxKeepaliveMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.rxKeepaliveMsgsAccum, 0)})
|
|
self.txPortalCapacity = append(self.txPortalCapacity, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.txPortalCapacityVal)})
|
|
self.txPortalSz = append(self.txPortalSz, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.txPortalSzVal)})
|
|
self.txPortalRxSz = append(self.txPortalRxSz, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.txPortalRxSzVal)})
|
|
self.retxMs = append(self.retxMs, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.retxMsVal)})
|
|
self.retxScale = append(self.retxScale, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.retxScaleVal)})
|
|
self.dupAcks = append(self.dupAcks, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.dupAcksAccum, 0)})
|
|
self.rxPortalSz = append(self.rxPortalSz, &util.Sample{Ts: now, V: atomic.LoadInt64(&self.rxPortalSzVal)})
|
|
self.dupRxBytes = append(self.dupRxBytes, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.dupRxBytesAccum, 0)})
|
|
self.dupRxMsgs = append(self.dupRxMsgs, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.dupRxMsgsAccum, 0)})
|
|
self.allocations = append(self.allocations, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.allocationsAccum, 0)})
|
|
self.errors = append(self.errors, &util.Sample{Ts: now, V: atomic.SwapInt64(&self.errorsAccum, 0)})
|
|
}
|