introduce statistics collection

This commit is contained in:
Jay
2026-04-24 13:48:52 -04:00
parent 4ac2c488ad
commit 6a3ba05fd5
14 changed files with 453 additions and 140 deletions
+5
View File
@@ -22,6 +22,7 @@ type Connection = transport.Connection
type ConnectionConfig = transport.ConnectionConfig
type RetryConfig = transport.RetryConfig
type ConnectionOption = transport.ConnectionOption
type ConnectionStats = transport.ConnectionStats
// Outbound Pool types
@@ -33,6 +34,8 @@ type OutboundWorkerOption = outbound.WorkerOption
type OutboundInboxMessage = outbound.InboxMessage
type OutboundPoolEvent = outbound.PoolEvent
type OutboundPoolEventKind = outbound.PoolEventKind
type OutboundPoolStats = outbound.PoolStats
type OutboundWorkerStats = outbound.WorkerStats
// Pool event constants
@@ -54,6 +57,8 @@ type InboundWorkerExitKind = inbound.WorkerExitKind
type InboundInboxMessage = inbound.InboxMessage
type InboundPoolEvent = inbound.PoolEvent
type InboundPoolEventKind = inbound.PoolEventKind
type InboundPoolStats = inbound.PoolStats
type InboundWorkerStats = inbound.WorkerStats
// Inbound Pool event constants
+76 -1
View File
@@ -8,6 +8,7 @@ import (
"git.wisehodl.dev/jay/go-honeybee/types"
"log/slog"
"sync"
"sync/atomic"
"time"
)
@@ -36,6 +37,24 @@ type PoolEvent struct {
Kind PoolEventKind
}
type PoolStats struct {
ChanInbox int
ChanEvents int
ChanErrors int
TotalReceived uint64
TotalSent uint64
PeerCount int
PeerStats []PeerStats
}
type PeerStats struct {
ID string
Worker WorkerStats
Connection transport.ConnectionStats
}
type InboxMessage struct {
ID string
Data []byte
@@ -46,6 +65,7 @@ type PoolPlugin struct {
Inbox chan<- InboxMessage
Events chan<- PoolEvent
Errors chan<- error
InboxCounter *atomic.Uint64
OnExit OnExitFunction
Handler slog.Handler
}
@@ -70,6 +90,9 @@ type Pool struct {
events chan PoolEvent
errors chan error
inboxCounter *atomic.Uint64
outgoingCount *atomic.Uint64
config *PoolConfig
handler slog.Handler
logger *slog.Logger
@@ -123,6 +146,8 @@ func NewPool(ctx context.Context, id string, config *PoolConfig, handler slog.Ha
inbox: make(chan InboxMessage, config.InboxBufferSize),
events: make(chan PoolEvent, config.EventsBufferSize),
errors: make(chan error, config.ErrorsBufferSize),
inboxCounter: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
config: config,
handler: handler,
logger: logger,
@@ -153,6 +178,49 @@ func (p *Pool) Errors() <-chan error {
return p.errors
}
func (p *Pool) Stats() PoolStats {
p.mu.RLock()
defer p.mu.RUnlock()
count := len(p.peers)
peerStats := make([]PeerStats, 0, count)
for id, peer := range p.peers {
peerStats = append(peerStats, PeerStats{
ID: id,
Worker: peer.worker.Stats(),
Connection: peer.conn.Stats(),
})
}
return PoolStats{
ChanInbox: len(p.inbox),
ChanEvents: len(p.events),
ChanErrors: len(p.errors),
TotalReceived: p.inboxCounter.Load(),
TotalSent: p.outgoingCount.Load(),
PeerCount: len(p.peers),
PeerStats: peerStats,
}
}
func (p *Pool) PeerStats(id string) (PeerStats, error) {
p.mu.RLock()
defer p.mu.RUnlock()
peer, exists := p.peers[id]
if !exists {
return PeerStats{}, ErrPeerNotFound
}
return PeerStats{
ID: id,
Worker: peer.worker.Stats(),
Connection: peer.conn.Stats(),
}, nil
}
func (p *Pool) Close() {
if p.logger != nil {
p.logger.Debug("closing")
@@ -266,7 +334,13 @@ func (p *Pool) Send(id string, data []byte) error {
return ErrPeerNotFound
}
return peer.worker.Send(data)
err := peer.worker.Send(data)
if err != nil {
return err
}
p.outgoingCount.Add(1)
return nil
}
// addLocked constructs and registers a peer. Caller must hold p.mu write lock.
@@ -318,6 +392,7 @@ func (p *Pool) addLocked(id string, socket types.Socket) error {
Inbox: p.inbox,
Events: p.events,
Errors: p.errors,
InboxCounter: p.inboxCounter,
OnExit: onExit,
Handler: p.handler,
}
+47 -7
View File
@@ -8,6 +8,7 @@ import (
"git.wisehodl.dev/jay/go-honeybee/types"
"log/slog"
"sync"
"sync/atomic"
"time"
)
@@ -15,6 +16,7 @@ type Worker interface {
Start(pool PoolPlugin)
Stop()
Send(data []byte) error
Stats() WorkerStats
}
type WorkerExitKind string
@@ -26,13 +28,31 @@ const (
ExitPolicy WorkerExitKind = "policy"
)
type WorkerStats struct {
ChanIncoming int
ChanQueue int
ChanForwarder int
TotalProcessed uint64
TotalDropped uint64
TotalSent uint64
}
type DefaultWorker struct {
id string
conn *transport.Connection
heartbeat chan struct{}
config *WorkerConfig
toQueue chan types.ReceivedMessage
toForwarder chan types.ReceivedMessage
processedCount *atomic.Uint64
droppedCount *atomic.Uint64
outgoingCount *atomic.Uint64
ctx context.Context
cancel context.CancelFunc
config *WorkerConfig
logger *slog.Logger
}
@@ -55,6 +75,11 @@ func NewWorker(
id: id,
conn: conn,
heartbeat: make(chan struct{}),
toQueue: make(chan types.ReceivedMessage, 256),
toForwarder: make(chan types.ReceivedMessage, 256),
processedCount: &atomic.Uint64{},
droppedCount: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
config: config,
ctx: wctx,
cancel: cancel,
@@ -67,15 +92,12 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
w.logger.Debug("starting")
}
toQueue := make(chan types.ReceivedMessage, 256)
toForwarder := make(chan types.ReceivedMessage, 256)
var wg sync.WaitGroup
wg.Add(5)
go func() {
defer wg.Done()
RunReader(w.ctx, pool.OnExit, w.conn, toQueue, w.heartbeat, w.logger)
RunReader(w.ctx, pool.OnExit, w.conn, w.toQueue, w.heartbeat, w.logger)
}()
go func() {
@@ -85,12 +107,12 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
go func() {
defer wg.Done()
queue.RunQueue(w.id, w.ctx, toQueue, toForwarder, w.config.MaxQueueSize)
queue.RunQueue(w.id, w.ctx, w.toQueue, w.toForwarder, w.config.MaxQueueSize, w.droppedCount)
}()
go func() {
defer wg.Done()
RunForwarder(w.id, w.ctx, toForwarder, pool.Inbox)
RunForwarder(w.id, w.ctx, w.toForwarder, pool.Inbox, w.processedCount, pool.InboxCounter)
}()
go func() {
@@ -126,9 +148,23 @@ func (w *DefaultWorker) Send(data []byte) error {
case <-w.ctx.Done():
}
w.outgoingCount.Add(1)
return nil
}
func (w *DefaultWorker) Stats() WorkerStats {
return WorkerStats{
ChanIncoming: len(w.conn.Incoming()),
ChanQueue: len(w.toQueue),
ChanForwarder: len(w.toForwarder),
TotalProcessed: w.processedCount.Load(),
TotalDropped: w.droppedCount.Load(),
TotalSent: w.outgoingCount.Load(),
}
}
func RunReader(
ctx context.Context,
onPeerClose OnExitFunction,
@@ -217,6 +253,8 @@ func RunForwarder(
ctx context.Context,
messages <-chan types.ReceivedMessage,
inbox chan<- InboxMessage,
workerProcessedCount *atomic.Uint64,
poolInboxCount *atomic.Uint64,
) {
for {
select {
@@ -235,6 +273,8 @@ func RunForwarder(
Data: msg.Data,
ReceivedAt: msg.ReceivedAt,
}:
workerProcessedCount.Add(1)
poolInboxCount.Add(1)
}
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/types"
"sync/atomic"
"testing"
"time"
)
@@ -16,7 +17,7 @@ func TestRunForwarder(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go RunForwarder(id, ctx, messages, inbox)
go RunForwarder(id, ctx, messages, inbox, &atomic.Uint64{}, &atomic.Uint64{})
messages <- types.ReceivedMessage{Data: []byte("hello"), ReceivedAt: time.Now()}
+1
View File
@@ -47,6 +47,7 @@ func setupWorkerTest(t *testing.T) workerTestVars {
OnExit: func(kind WorkerExitKind) {
once.Do(func() { exitKind.Store(kind) })
},
InboxCounter: &atomic.Uint64{},
}
wg := &sync.WaitGroup{}
+73 -1
View File
@@ -7,6 +7,7 @@ import (
"git.wisehodl.dev/jay/go-honeybee/types"
"log/slog"
"sync"
"sync/atomic"
"time"
)
@@ -24,6 +25,23 @@ type PoolEvent struct {
Kind PoolEventKind
}
type PoolStats struct {
ChanInbox int
ChanEvents int
ChanErrors int
TotalReceived uint64
TotalSent uint64
PeerCount int
PeerStats []PeerStats
}
type PeerStats struct {
ID string
Worker WorkerStats
}
type InboxMessage struct {
ID string
Data []byte
@@ -35,6 +53,7 @@ type PoolPlugin struct {
Inbox chan<- InboxMessage
Events chan<- PoolEvent
Errors chan<- error
InboxCounter *atomic.Uint64
Dialer types.Dialer
ConnectionConfig *transport.ConnectionConfig
Handler slog.Handler
@@ -58,6 +77,9 @@ type Pool struct {
events chan PoolEvent
errors chan error
inboxCounter *atomic.Uint64
outgoingCount *atomic.Uint64
dialer types.Dialer
config *PoolConfig
handler slog.Handler
@@ -108,6 +130,8 @@ func NewPool(ctx context.Context, id string, config *PoolConfig, handler slog.Ha
inbox: make(chan InboxMessage, config.InboxBufferSize),
events: make(chan PoolEvent, config.EventsBufferSize),
errors: make(chan error, config.ErrorsBufferSize),
inboxCounter: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
dialer: transport.NewDialer(),
config: config,
handler: handler,
@@ -138,6 +162,47 @@ func (p *Pool) Errors() <-chan error {
return p.errors
}
func (p *Pool) Stats() PoolStats {
p.mu.RLock()
defer p.mu.RUnlock()
count := len(p.peers)
peerStats := make([]PeerStats, 0, count)
for id, peer := range p.peers {
peerStats = append(peerStats, PeerStats{
ID: id,
Worker: peer.worker.Stats(),
})
}
return PoolStats{
ChanInbox: len(p.inbox),
ChanEvents: len(p.events),
ChanErrors: len(p.errors),
TotalReceived: p.inboxCounter.Load(),
TotalSent: p.outgoingCount.Load(),
PeerCount: len(p.peers),
PeerStats: peerStats,
}
}
func (p *Pool) PeerStats(id string) (PeerStats, error) {
p.mu.RLock()
defer p.mu.RUnlock()
peer, exists := p.peers[id]
if !exists {
return PeerStats{}, ErrPeerNotFound
}
return PeerStats{
ID: id,
Worker: peer.worker.Stats(),
}, nil
}
func (p *Pool) SetDialer(d types.Dialer) {
if d == nil {
panic("dialer cannot be nil")
@@ -214,6 +279,7 @@ func (p *Pool) Connect(id string) error {
Inbox: p.inbox,
Events: p.events,
Errors: p.errors,
InboxCounter: p.inboxCounter,
Dialer: p.dialer,
ConnectionConfig: p.config.ConnectionConfig,
Handler: p.handler,
@@ -284,5 +350,11 @@ func (p *Pool) Send(id string, data []byte) error {
return NewPoolError(ErrPeerNotFound)
}
return peer.worker.Send(data)
err = peer.worker.Send(data)
if err != nil {
return err
}
p.outgoingCount.Add(1)
return nil
}
+71 -5
View File
@@ -18,12 +18,37 @@ type Worker interface {
Start(pool PoolPlugin)
Stop()
Send(data []byte) error
Stats() WorkerStats
}
type WorkerStats struct {
IncomingAvailable bool
ChanIncoming int
ChanQueue int
ChanForwarder int
ConnectionAvailable bool
Connection transport.ConnectionStats
TotalProcessed uint64
TotalDropped uint64
TotalSent uint64
TotalRestarts uint64
}
type DefaultWorker struct {
id string
conn atomic.Pointer[transport.Connection]
heartbeat chan struct{}
toQueue chan types.ReceivedMessage
toForwarder chan types.ReceivedMessage
processedCount *atomic.Uint64
droppedCount *atomic.Uint64
outgoingCount *atomic.Uint64
restartCount *atomic.Uint64
config *WorkerConfig
ctx context.Context
cancel context.CancelFunc
@@ -48,6 +73,12 @@ func NewWorker(
id: id,
config: config,
heartbeat: make(chan struct{}),
toQueue: make(chan types.ReceivedMessage, 256),
toForwarder: make(chan types.ReceivedMessage, 256),
processedCount: &atomic.Uint64{},
droppedCount: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
restartCount: &atomic.Uint64{},
ctx: wctx,
cancel: wcancel,
logger: logger,
@@ -63,8 +94,6 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
dial := make(chan struct{}, 1)
newConn := make(chan *transport.Connection, 1)
toQueue := make(chan types.ReceivedMessage, 256)
toForwarder := make(chan types.ReceivedMessage, 256)
keepalive := make(chan struct{}, 1)
var wg sync.WaitGroup
@@ -82,12 +111,12 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
go func() {
defer wg.Done()
queue.RunQueue(w.id, w.ctx, toQueue, toForwarder, w.config.MaxQueueSize)
queue.RunQueue(w.id, w.ctx, w.toQueue, w.toForwarder, w.config.MaxQueueSize, w.droppedCount)
}()
go func() {
defer wg.Done()
RunForwarder(w.id, w.ctx, toForwarder, pool.Inbox)
RunForwarder(w.id, w.ctx, w.toForwarder, pool.Inbox, w.processedCount, pool.InboxCounter)
}()
go func() {
@@ -95,12 +124,13 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
session := &Session{
id: w.id,
connPtr: &w.conn,
messages: toQueue,
messages: w.toQueue,
heartbeat: w.heartbeat,
dial: dial,
keepalive: keepalive,
newConn: newConn,
reconnectDelay: w.config.ReconnectDelay,
restartCount: w.restartCount,
logger: w.logger,
}
session.Start(w.ctx, pool)
@@ -140,9 +170,39 @@ func (w *DefaultWorker) Send(data []byte) error {
case <-w.ctx.Done():
}
w.outgoingCount.Add(1)
return nil
}
func (w *DefaultWorker) Stats() WorkerStats {
connectionAvailable := false
incomingLen := 0
connStats := transport.ConnectionStats{}
conn := w.conn.Load()
if conn != nil {
connectionAvailable = true
incomingLen = len(conn.Incoming())
connStats = conn.Stats()
}
return WorkerStats{
IncomingAvailable: connectionAvailable,
ChanIncoming: incomingLen,
ChanQueue: len(w.toQueue),
ChanForwarder: len(w.toForwarder),
ConnectionAvailable: connectionAvailable,
Connection: connStats,
TotalProcessed: w.processedCount.Load(),
TotalDropped: w.droppedCount.Load(),
TotalRestarts: w.restartCount.Load(),
TotalSent: w.outgoingCount.Load(),
}
}
type Session struct {
id string
connPtr *atomic.Pointer[transport.Connection]
@@ -155,6 +215,7 @@ type Session struct {
newConn <-chan *transport.Connection
reconnectDelay time.Duration
restartCount *atomic.Uint64
logger *slog.Logger
}
@@ -246,6 +307,7 @@ func (s *Session) Start(
// refresh session
time.Sleep(s.reconnectDelay)
s.restartCount.Add(1)
}
}
@@ -346,6 +408,8 @@ func RunForwarder(
ctx context.Context,
messages <-chan types.ReceivedMessage,
inbox chan<- InboxMessage,
workerProcessedCount *atomic.Uint64,
poolInboxCount *atomic.Uint64,
) {
for {
select {
@@ -364,6 +428,8 @@ func RunForwarder(
Data: msg.Data,
ReceivedAt: msg.ReceivedAt,
}:
workerProcessedCount.Add(1)
poolInboxCount.Add(1)
}
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/types"
"sync/atomic"
"testing"
"time"
)
@@ -16,7 +17,7 @@ func TestRunForwarder(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go RunForwarder(id, ctx, messages, inbox)
go RunForwarder(id, ctx, messages, inbox, &atomic.Uint64{}, &atomic.Uint64{})
messages <- types.ReceivedMessage{Data: []byte("hello"), ReceivedAt: time.Now()}
+2
View File
@@ -24,6 +24,7 @@ func TestWorkerSend(t *testing.T) {
cancel: cancel,
id: "wss://test",
heartbeat: heartbeat,
outgoingCount: &atomic.Uint64{},
}
w.conn.Store(conn)
defer w.cancel()
@@ -68,6 +69,7 @@ func TestWorkerSend(t *testing.T) {
cancel: cancel,
id: "wss://test",
heartbeat: heartbeat,
outgoingCount: &atomic.Uint64{},
}
w.conn.Store(conn)
defer w.cancel()
+4
View File
@@ -218,6 +218,7 @@ func TestRunSessionDisconnect(t *testing.T) {
dial: v.dial,
keepalive: v.keepalive,
newConn: v.newConn,
restartCount: &atomic.Uint64{},
}
go session.Start(ctx, pool)
@@ -244,6 +245,7 @@ func TestRunSessionDisconnect(t *testing.T) {
dial: v.dial,
keepalive: v.keepalive,
newConn: v.newConn,
restartCount: &atomic.Uint64{},
}
go session.Start(ctx, pool)
@@ -273,6 +275,7 @@ func TestRunSessionDisconnect(t *testing.T) {
dial: v.dial,
keepalive: v.keepalive,
newConn: v.newConn,
restartCount: &atomic.Uint64{},
}
go session.Start(ctx, pool)
@@ -310,6 +313,7 @@ func TestRunSessionDisconnect(t *testing.T) {
dial: v.dial,
keepalive: v.keepalive,
newConn: v.newConn,
restartCount: &atomic.Uint64{},
}
go session.Start(ctx, pool)
+8
View File
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"net/http"
"sync"
"sync/atomic"
"testing"
"time"
)
@@ -28,6 +29,7 @@ func makeWorkerContext(t *testing.T) (
Inbox: inbox,
Events: events,
Errors: errors,
InboxCounter: &atomic.Uint64{},
}
return
}
@@ -43,6 +45,12 @@ func makeWorker(t *testing.T, ctx context.Context, cancel context.CancelFunc) *D
id: "wss://test",
config: config,
heartbeat: make(chan struct{}),
toQueue: make(chan types.ReceivedMessage, 256),
toForwarder: make(chan types.ReceivedMessage, 256),
processedCount: &atomic.Uint64{},
droppedCount: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
restartCount: &atomic.Uint64{},
}
}
+3
View File
@@ -3,6 +3,7 @@ package queue
import (
"context"
"git.wisehodl.dev/jay/go-honeybee/types"
"sync/atomic"
)
func RunQueue(
@@ -11,6 +12,7 @@ func RunQueue(
in <-chan types.ReceivedMessage,
out chan<- types.ReceivedMessage,
maxQueueSize int,
droppedCount *atomic.Uint64,
) {
var next types.ReceivedMessage
var queue messageQueue
@@ -37,6 +39,7 @@ func RunQueue(
if maxQueueSize > 0 && queue.len() >= maxQueueSize {
// drop oldest message
_ = queue.pop()
droppedCount.Add(1)
}
// add new message
queue.push(msg)
+4 -3
View File
@@ -5,6 +5,7 @@ import (
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/types"
"github.com/stretchr/testify/assert"
"sync/atomic"
"testing"
"time"
)
@@ -17,7 +18,7 @@ func TestRunQueue(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go RunQueue(id, ctx, inChan, outChan, 0)
go RunQueue(id, ctx, inChan, outChan, 0, &atomic.Uint64{})
inChan <- types.ReceivedMessage{Data: []byte("hello"), ReceivedAt: time.Now()}
@@ -49,7 +50,7 @@ func TestRunQueue(t *testing.T) {
}
}()
go RunQueue(id, ctx, inChan, gatedInbox, 2)
go RunQueue(id, ctx, inChan, gatedInbox, 2, &atomic.Uint64{})
// send three messages while the gated inbox is blocked
inChan <- types.ReceivedMessage{Data: []byte("first"), ReceivedAt: time.Now()}
@@ -87,7 +88,7 @@ func TestRunQueue(t *testing.T) {
done := make(chan struct{})
go func() {
RunQueue(id, ctx, inChan, outChan, 0)
RunQueue(id, ctx, inChan, outChan, 0, &atomic.Uint64{})
close(done)
}()
+34
View File
@@ -8,6 +8,7 @@ import (
"math/rand"
"net/url"
"sync"
"sync/atomic"
"time"
"git.wisehodl.dev/jay/go-honeybee/types"
@@ -38,6 +39,15 @@ func (s ConnectionState) String() string {
}
}
type ConnectionStats struct {
ChanIncoming int
ChanErrors int
TotalReceived uint64
TotalSent uint64
TotalHeartbeats uint64
}
type Connection struct {
url *url.URL
dialer types.Dialer
@@ -50,6 +60,10 @@ type Connection struct {
errors chan error
done chan struct{}
incomingCount *atomic.Uint64
outgoingCount *atomic.Uint64
heartbeatCount *atomic.Uint64
state ConnectionState
wg sync.WaitGroup
@@ -83,6 +97,9 @@ func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger)
incoming: make(chan []byte, config.IncomingBufferSize),
heartbeat: make(chan struct{}, 1),
errors: make(chan error, config.ErrorsBufferSize),
incomingCount: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
heartbeatCount: &atomic.Uint64{},
state: StateDisconnected,
done: make(chan struct{}),
}
@@ -114,6 +131,9 @@ func NewConnectionFromSocket(
incoming: make(chan []byte, config.IncomingBufferSize),
heartbeat: make(chan struct{}, 1),
errors: make(chan error, config.ErrorsBufferSize),
incomingCount: &atomic.Uint64{},
outgoingCount: &atomic.Uint64{},
heartbeatCount: &atomic.Uint64{},
state: StateConnected,
done: make(chan struct{}),
}
@@ -336,6 +356,7 @@ func (c *Connection) startReader() {
case <-c.done:
return
case c.incoming <- data:
c.incomingCount.Add(1)
}
}
@@ -348,6 +369,7 @@ func (c *Connection) setupPongHandler() {
c.socket.SetPongHandler(func(appData string) error {
select {
case c.heartbeat <- struct{}{}:
c.heartbeatCount.Add(1)
default:
}
return nil
@@ -410,6 +432,8 @@ func (c *Connection) Send(data []byte) error {
return NewConnectionError(fmt.Errorf("%w: %w", ErrWriteFailed, err))
}
c.outgoingCount.Add(1)
return nil
}
@@ -431,6 +455,16 @@ func (c *Connection) State() ConnectionState {
return c.state
}
func (c *Connection) Stats() ConnectionStats {
return ConnectionStats{
ChanIncoming: len(c.incoming),
ChanErrors: len(c.errors),
TotalReceived: c.incomingCount.Load(),
TotalSent: c.outgoingCount.Load(),
TotalHeartbeats: c.heartbeatCount.Load(),
}
}
func (c *Connection) SetDialer(d types.Dialer) {
c.dialer = d
}