summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gopackage/ddp/ddp_stats.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gopackage/ddp/ddp_stats.go')
-rw-r--r--vendor/github.com/gopackage/ddp/ddp_stats.go321
1 files changed, 321 insertions, 0 deletions
diff --git a/vendor/github.com/gopackage/ddp/ddp_stats.go b/vendor/github.com/gopackage/ddp/ddp_stats.go
new file mode 100644
index 00000000..1546b547
--- /dev/null
+++ b/vendor/github.com/gopackage/ddp/ddp_stats.go
@@ -0,0 +1,321 @@
+package ddp
+
+import (
+ "encoding/hex"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "sync"
+ "time"
+)
+
+// Gather statistics about a DDP connection.
+
+// ---------------------------------------------------------
+// io utilities
+//
+// This is generic - should be moved into a stand alone lib
+// ---------------------------------------------------------
+
+// ReaderProxy provides common tooling for structs that manage an io.Reader.
+type ReaderProxy struct {
+ reader io.Reader
+}
+
+// NewReaderProxy creates a new proxy for the provided reader.
+func NewReaderProxy(reader io.Reader) *ReaderProxy {
+ return &ReaderProxy{reader}
+}
+
+// SetReader sets the reader on the proxy.
+func (r *ReaderProxy) SetReader(reader io.Reader) {
+ r.reader = reader
+}
+
+// WriterProxy provides common tooling for structs that manage an io.Writer.
+type WriterProxy struct {
+ writer io.Writer
+}
+
+// NewWriterProxy creates a new proxy for the provided writer.
+func NewWriterProxy(writer io.Writer) *WriterProxy {
+ return &WriterProxy{writer}
+}
+
+// SetWriter sets the writer on the proxy.
+func (w *WriterProxy) SetWriter(writer io.Writer) {
+ w.writer = writer
+}
+
+// Logging data types
+const (
+ DataByte = iota // data is raw []byte
+ DataText // data is string data
+)
+
+// Logger logs data from i/o sources.
+type Logger struct {
+ // Active is true if the logger should be logging reads
+ Active bool
+ // Truncate is >0 to indicate the number of characters to truncate output
+ Truncate int
+
+ logger *log.Logger
+ dtype int
+}
+
+// NewLogger creates a new i/o logger.
+func NewLogger(logger *log.Logger, active bool, dataType int, truncate int) Logger {
+ return Logger{logger: logger, Active: active, dtype: dataType, Truncate: truncate}
+}
+
+// Log logs the current i/o operation and returns the read and error for
+// easy call chaining.
+func (l *Logger) Log(p []byte, n int, err error) (int, error) {
+ if l.Active && err == nil {
+ limit := n
+ truncated := false
+ if l.Truncate > 0 && l.Truncate < limit {
+ limit = l.Truncate
+ truncated = true
+ }
+ switch l.dtype {
+ case DataText:
+ if truncated {
+ l.logger.Printf("[%d] %s...", n, string(p[:limit]))
+ } else {
+ l.logger.Printf("[%d] %s", n, string(p[:limit]))
+ }
+ case DataByte:
+ fallthrough
+ default:
+ l.logger.Println(hex.Dump(p[:limit]))
+ }
+ }
+ return n, err
+}
+
+// ReaderLogger logs data from any io.Reader.
+// ReaderLogger wraps a Reader and passes data to the actual data consumer.
+type ReaderLogger struct {
+ Logger
+ ReaderProxy
+}
+
+// NewReaderDataLogger creates an active binary data logger with a default
+// log.Logger and a '->' prefix.
+func NewReaderDataLogger(reader io.Reader) *ReaderLogger {
+ logger := log.New(os.Stdout, "<- ", log.LstdFlags)
+ return NewReaderLogger(reader, logger, true, DataByte, 0)
+}
+
+// NewReaderTextLogger creates an active binary data logger with a default
+// log.Logger and a '->' prefix.
+func NewReaderTextLogger(reader io.Reader) *ReaderLogger {
+ logger := log.New(os.Stdout, "<- ", log.LstdFlags)
+ return NewReaderLogger(reader, logger, true, DataText, 80)
+}
+
+// NewReaderLogger creates a Reader logger for the provided parameters.
+func NewReaderLogger(reader io.Reader, logger *log.Logger, active bool, dataType int, truncate int) *ReaderLogger {
+ return &ReaderLogger{ReaderProxy: *NewReaderProxy(reader), Logger: NewLogger(logger, active, dataType, truncate)}
+}
+
+// Read logs the read bytes and passes them to the wrapped reader.
+func (r *ReaderLogger) Read(p []byte) (int, error) {
+ n, err := r.reader.Read(p)
+ return r.Log(p, n, err)
+}
+
+// WriterLogger logs data from any io.Writer.
+// WriterLogger wraps a Writer and passes data to the actual data producer.
+type WriterLogger struct {
+ Logger
+ WriterProxy
+}
+
+// NewWriterDataLogger creates an active binary data logger with a default
+// log.Logger and a '->' prefix.
+func NewWriterDataLogger(writer io.Writer) *WriterLogger {
+ logger := log.New(os.Stdout, "-> ", log.LstdFlags)
+ return NewWriterLogger(writer, logger, true, DataByte, 0)
+}
+
+// NewWriterTextLogger creates an active binary data logger with a default
+// log.Logger and a '->' prefix.
+func NewWriterTextLogger(writer io.Writer) *WriterLogger {
+ logger := log.New(os.Stdout, "-> ", log.LstdFlags)
+ return NewWriterLogger(writer, logger, true, DataText, 80)
+}
+
+// NewWriterLogger creates a Reader logger for the provided parameters.
+func NewWriterLogger(writer io.Writer, logger *log.Logger, active bool, dataType int, truncate int) *WriterLogger {
+ return &WriterLogger{WriterProxy: *NewWriterProxy(writer), Logger: NewLogger(logger, active, dataType, truncate)}
+}
+
+// Write logs the written bytes and passes them to the wrapped writer.
+func (w *WriterLogger) Write(p []byte) (int, error) {
+ if w.writer != nil {
+ n, err := w.writer.Write(p)
+ return w.Log(p, n, err)
+ }
+ return 0, nil
+}
+
+// Stats tracks statistics for i/o operations. Stats are produced from a
+// of a running stats agent.
+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 stats tracker with start time set to now.
+func NewStatsTracker() *StatsTracker {
+ return &StatsTracker{start: time.Now(), lock: new(sync.Mutex)}
+}
+
+// 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 sets all of the 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
+ ReaderProxy
+}
+
+// NewReaderStats creates a ReaderStats object for the provided reader.
+func NewReaderStats(reader io.Reader) *ReaderStats {
+ return &ReaderStats{ReaderProxy: *NewReaderProxy(reader), StatsTracker: *NewStatsTracker()}
+}
+
+// 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
+ WriterProxy
+}
+
+// NewWriterStats creates a WriterStats object for the provided writer.
+func NewWriterStats(writer io.Writer) *WriterStats {
+ return &WriterStats{WriterProxy: *NewWriterProxy(writer), StatsTracker: *NewStatsTracker()}
+}
+
+// Write passes through a write collecting statistics.
+func (w *WriterStats) Write(p []byte) (int, error) {
+ if w.writer != nil {
+ return w.Op(w.writer.Write(p))
+ }
+ return 0, nil
+}