2018-02-01 14:13:24 +01:00
|
|
|
package accounting
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ncw/rclone/fs"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Stats is global statistics counter
|
|
|
|
Stats = NewStats()
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Set the function pointer up in fs
|
|
|
|
fs.CountError = Stats.Error
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatsInfo accounts all transfers
|
|
|
|
type StatsInfo struct {
|
2018-05-02 18:01:39 +02:00
|
|
|
mu sync.RWMutex
|
2018-02-01 14:13:24 +01:00
|
|
|
bytes int64
|
|
|
|
errors int64
|
|
|
|
lastError error
|
|
|
|
checks int64
|
2018-05-02 18:01:39 +02:00
|
|
|
checking *stringSet
|
2018-02-01 14:13:24 +01:00
|
|
|
transfers int64
|
2018-05-02 18:01:39 +02:00
|
|
|
transferring *stringSet
|
2018-01-22 19:53:18 +01:00
|
|
|
deletes int64
|
2018-02-01 14:13:24 +01:00
|
|
|
start time.Time
|
|
|
|
inProgress *inProgress
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewStats cretates an initialised StatsInfo
|
|
|
|
func NewStats() *StatsInfo {
|
|
|
|
return &StatsInfo{
|
2018-05-02 18:01:39 +02:00
|
|
|
checking: newStringSet(fs.Config.Checkers),
|
|
|
|
transferring: newStringSet(fs.Config.Transfers),
|
2018-02-01 14:13:24 +01:00
|
|
|
start: time.Now(),
|
|
|
|
inProgress: newInProgress(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// String convert the StatsInfo to a string for printing
|
|
|
|
func (s *StatsInfo) String() string {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
|
2018-02-01 14:13:24 +01:00
|
|
|
dt := time.Now().Sub(s.start)
|
|
|
|
dtSeconds := dt.Seconds()
|
|
|
|
speed := 0.0
|
|
|
|
if dt > 0 {
|
|
|
|
speed = float64(s.bytes) / dtSeconds
|
|
|
|
}
|
|
|
|
dtRounded := dt - (dt % (time.Second / 10))
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
|
|
|
|
if fs.Config.DataRateUnit == "bits" {
|
|
|
|
speed = speed * 8
|
|
|
|
}
|
|
|
|
|
2018-05-22 10:41:13 +02:00
|
|
|
_, _ = fmt.Fprintf(buf, `
|
2018-02-01 14:13:24 +01:00
|
|
|
Transferred: %10s (%s)
|
|
|
|
Errors: %10d
|
|
|
|
Checks: %10d
|
|
|
|
Transferred: %10d
|
|
|
|
Elapsed time: %10v
|
|
|
|
`,
|
|
|
|
fs.SizeSuffix(s.bytes).Unit("Bytes"), fs.SizeSuffix(speed).Unit(strings.Title(fs.Config.DataRateUnit)+"/s"),
|
|
|
|
s.errors,
|
|
|
|
s.checks,
|
|
|
|
s.transfers,
|
|
|
|
dtRounded)
|
2018-05-02 18:01:39 +02:00
|
|
|
|
|
|
|
// checking and transferring have their own locking so unlock
|
|
|
|
// here to prevent deadlock on GetBytes
|
|
|
|
s.mu.RUnlock()
|
|
|
|
|
|
|
|
if !s.checking.empty() {
|
2018-05-22 10:41:13 +02:00
|
|
|
_, _ = fmt.Fprintf(buf, "Checking:\n%s\n", s.checking)
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
2018-05-02 18:01:39 +02:00
|
|
|
if !s.transferring.empty() {
|
2018-05-22 10:41:13 +02:00
|
|
|
_, _ = fmt.Fprintf(buf, "Transferring:\n%s\n", s.transferring)
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log outputs the StatsInfo to the log
|
|
|
|
func (s *StatsInfo) Log() {
|
|
|
|
fs.LogLevelPrintf(fs.Config.StatsLogLevel, nil, "%v\n", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes updates the stats for bytes bytes
|
|
|
|
func (s *StatsInfo) Bytes(bytes int64) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.bytes += bytes
|
|
|
|
}
|
|
|
|
|
2018-04-21 23:03:27 +02:00
|
|
|
// GetBytes returns the number of bytes transferred so far
|
|
|
|
func (s *StatsInfo) GetBytes() int64 {
|
2018-07-22 11:33:19 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
2018-04-21 23:03:27 +02:00
|
|
|
return s.bytes
|
|
|
|
}
|
|
|
|
|
2018-02-01 14:13:24 +01:00
|
|
|
// Errors updates the stats for errors
|
|
|
|
func (s *StatsInfo) Errors(errors int64) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.errors += errors
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetErrors reads the number of errors
|
|
|
|
func (s *StatsInfo) GetErrors() int64 {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
return s.errors
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLastError returns the lastError
|
|
|
|
func (s *StatsInfo) GetLastError() error {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
return s.lastError
|
|
|
|
}
|
|
|
|
|
2018-01-22 19:53:18 +01:00
|
|
|
// Deletes updates the stats for deletes
|
|
|
|
func (s *StatsInfo) Deletes(deletes int64) int64 {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-01-22 19:53:18 +01:00
|
|
|
s.deletes += deletes
|
|
|
|
return s.deletes
|
|
|
|
}
|
|
|
|
|
2018-02-01 14:13:24 +01:00
|
|
|
// ResetCounters sets the counters (bytes, checks, errors, transfers) to 0
|
|
|
|
func (s *StatsInfo) ResetCounters() {
|
2018-07-22 11:33:19 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.bytes = 0
|
|
|
|
s.errors = 0
|
|
|
|
s.checks = 0
|
|
|
|
s.transfers = 0
|
2018-01-22 19:53:18 +01:00
|
|
|
s.deletes = 0
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ResetErrors sets the errors count to 0
|
|
|
|
func (s *StatsInfo) ResetErrors() {
|
2018-07-22 11:33:19 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.errors = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errored returns whether there have been any errors
|
|
|
|
func (s *StatsInfo) Errored() bool {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
return s.errors != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error adds a single error into the stats and assigns lastError
|
|
|
|
func (s *StatsInfo) Error(err error) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.errors++
|
|
|
|
s.lastError = err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checking adds a check into the stats
|
|
|
|
func (s *StatsInfo) Checking(remote string) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.checking.add(remote)
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// DoneChecking removes a check from the stats
|
|
|
|
func (s *StatsInfo) DoneChecking(remote string) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.checking.del(remote)
|
|
|
|
s.mu.Lock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.checks++
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetTransfers reads the number of transfers
|
|
|
|
func (s *StatsInfo) GetTransfers() int64 {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
return s.transfers
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transferring adds a transfer into the stats
|
|
|
|
func (s *StatsInfo) Transferring(remote string) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.transferring.add(remote)
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// DoneTransferring removes a transfer from the stats
|
|
|
|
//
|
|
|
|
// if ok is true then it increments the transfers count
|
|
|
|
func (s *StatsInfo) DoneTransferring(remote string, ok bool) {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.transferring.del(remote)
|
2018-02-01 14:13:24 +01:00
|
|
|
if ok {
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Lock()
|
2018-02-01 14:13:24 +01:00
|
|
|
s.transfers++
|
2018-05-02 18:01:39 +02:00
|
|
|
s.mu.Unlock()
|
2018-02-01 14:13:24 +01:00
|
|
|
}
|
|
|
|
}
|