summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gopackage/ddp/stats.go
blob: b2548098b645d930119acfaf4504753606419290 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package ddp

import (
	"fmt"
	"io"
	"sync"
	"time"
)

// Gather statistics about a DDP connection.

// Stats tracks statistics for i/o operations.
type Stats struct {
	// Bytes is the total number of bytes transferred.
	Bytes int64
	// Ops is the total number of i/o operations performed.
	Ops int64
	// Errors is the total number of i/o errors encountered.
	Errors int64
	// Runtime is the duration that stats have been gathered.
	Runtime time.Duration
}

// ClientStats displays combined statistics for the Client.
type ClientStats struct {
	// Reads provides statistics on the raw i/o network reads for the current connection.
	Reads *Stats
	// Reads provides statistics on the raw i/o network reads for the all client connections.
	TotalReads *Stats
	// Writes provides statistics on the raw i/o network writes for the current connection.
	Writes *Stats
	// Writes provides statistics on the raw i/o network writes for all the client connections.
	TotalWrites *Stats
	// Reconnects is the number of reconnections the client has made.
	Reconnects int64
	// PingsSent is the number of pings sent by the client
	PingsSent int64
	// PingsRecv is the number of pings received by the client
	PingsRecv int64
}

// String produces a compact string representation of the client stats.
func (stats *ClientStats) String() string {
	i := stats.Reads
	ti := stats.TotalReads
	o := stats.Writes
	to := stats.TotalWrites
	totalRun := (ti.Runtime * 1000000) / 1000000
	run := (i.Runtime * 1000000) / 1000000
	return fmt.Sprintf("bytes: %d/%d##%d/%d ops: %d/%d##%d/%d err: %d/%d##%d/%d reconnects: %d pings: %d/%d uptime: %v##%v",
		i.Bytes, o.Bytes,
		ti.Bytes, to.Bytes,
		i.Ops, o.Ops,
		ti.Ops, to.Ops,
		i.Errors, o.Errors,
		ti.Errors, to.Errors,
		stats.Reconnects,
		stats.PingsRecv, stats.PingsSent,
		run, totalRun)
}

// CollectionStats combines statistics about a collection.
type CollectionStats struct {
	Name  string // Name of the collection
	Count int    // Count is the total number of documents in the collection
}

// String produces a compact string representation of the collection stat.
func (s *CollectionStats) String() string {
	return fmt.Sprintf("%s[%d]", s.Name, s.Count)
}

// StatsTracker provides the basic tooling for tracking i/o stats.
type StatsTracker struct {
	bytes  int64
	ops    int64
	errors int64
	start  time.Time
	lock   sync.Mutex
}

// NewStatsTracker create a new tracker with start time set to now.
func NewStatsTracker() *StatsTracker {
	return &StatsTracker{start: time.Now()}
}

// Op records an i/o operation. The parameters are passed through to
// allow easy chaining.
func (t *StatsTracker) Op(n int, err error) (int, error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ops++
	if err == nil {
		t.bytes += int64(n)
	} else {
		if err == io.EOF {
			// I don't think we should log EOF stats as an error
		} else {
			t.errors++
		}
	}

	return n, err
}

// Snapshot takes a snapshot of the current Reader statistics.
func (t *StatsTracker) Snapshot() *Stats {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.snap()
}

// Reset all stats to initial values.
func (t *StatsTracker) Reset() *Stats {
	t.lock.Lock()
	defer t.lock.Unlock()

	stats := t.snap()
	t.bytes = 0
	t.ops = 0
	t.errors = 0
	t.start = time.Now()

	return stats
}

func (t *StatsTracker) snap() *Stats {
	return &Stats{Bytes: t.bytes, Ops: t.ops, Errors: t.errors, Runtime: time.Since(t.start)}
}

// ReaderStats tracks statistics on any io.Reader.
// ReaderStats wraps a Reader and passes data to the actual data consumer.
type ReaderStats struct {
	StatsTracker
	Reader io.Reader
}

// NewReaderStats creates a ReaderStats object for the provided Reader.
func NewReaderStats(reader io.Reader) *ReaderStats {
	r := &ReaderStats{Reader: reader}
	r.Reset()
	return r
}

// Read passes through a read collecting statistics and logging activity.
func (r *ReaderStats) Read(p []byte) (int, error) {
	return r.Op(r.Reader.Read(p))
}

// WriterStats tracks statistics on any io.Writer.
// WriterStats wraps a Writer and passes data to the actual data producer.
type WriterStats struct {
	StatsTracker
	Writer io.Writer
}

// NewWriterStats creates a WriterStats object for the provided Writer.
func NewWriterStats(writer io.Writer) *WriterStats {
	w := &WriterStats{Writer: writer}
	w.Reset()
	return w
}

// Write collects Writer statistics.
func (w *WriterStats) Write(p []byte) (int, error) {
	if w.Writer != nil {
		return w.Op(w.Writer.Write(p))
	}
	return 0, nil
}