accounting: fix layout of stats - fixes #2910

This fixes several things wrong with the layout of the stats.

Transfers which haven't started are printed in the same format as
those which have so the stats with `--progress` don't show horrible
artifacts.

Checkers and transfers now get a ": checkers" and ": transfers" label
on the end of the stats line.  Transfers will have the transfer stats
when the transfer has started instead of this.

There was a bug in the routine which shortened the file names (it
always produces strings 1 too long).  This is now fixed with a test.

The formatting string was wrong with a fixed width of 45 - this is now
replaces with the value of `--stats-file-name-length`.

This also meant that there were unecessary leading spaces in the file
names.  So the default `--stats-file-name-length` was raised to 45
from 40.
s3-about
Nick Craig-Wood 2019-01-14 16:12:39 +00:00
parent bd11d3cb62
commit 5990573ccd
5 changed files with 83 additions and 20 deletions

View File

@ -6,6 +6,7 @@ import (
"io"
"sync"
"time"
"unicode/utf8"
"github.com/ncw/rclone/fs"
"github.com/ncw/rclone/fs/asyncreader"
@ -243,6 +244,24 @@ func (acc *Account) eta() (etaDuration time.Duration, ok bool) {
return eta(acc.bytes, acc.size, acc.avg)
}
// shortenName shortens in to size runes long
// If size <= 0 then in is left untouched
func shortenName(in string, size int) string {
if size <= 0 {
return in
}
if utf8.RuneCountInString(in) <= size {
return in
}
name := []rune(in)
size-- // don't count elipsis rune
suffixLength := size / 2
prefixLength := size - suffixLength
suffixStart := len(name) - suffixLength
name = append(append(name[:prefixLength], '…'), name[suffixStart:]...)
return string(name)
}
// String produces stats for this file
func (acc *Account) String() string {
a, b := acc.progress()
@ -257,16 +276,6 @@ func (acc *Account) String() string {
}
}
name := []rune(acc.name)
if fs.Config.StatsFileNameLength > 0 {
if len(name) > fs.Config.StatsFileNameLength {
suffixLength := fs.Config.StatsFileNameLength / 2
prefixLength := fs.Config.StatsFileNameLength - suffixLength
suffixStart := len(name) - suffixLength
name = append(append(name[:prefixLength], '…'), name[suffixStart:]...)
}
}
if fs.Config.DataRateUnit == "bits" {
cur = cur * 8
}
@ -276,11 +285,11 @@ func (acc *Account) String() string {
percentageDone = int(100 * float64(a) / float64(b))
}
done := fmt.Sprintf("%2d%% /%s", percentageDone, fs.SizeSuffix(b))
return fmt.Sprintf("%45s: %s, %s/s, %s",
string(name),
done,
return fmt.Sprintf("%*s:%3d%% /%s, %s/s, %s",
fs.Config.StatsFileNameLength,
shortenName(acc.name, fs.Config.StatsFileNameLength),
percentageDone,
fs.SizeSuffix(b),
fs.SizeSuffix(cur),
etas,
)

View File

@ -2,10 +2,12 @@ package accounting
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
"unicode/utf8"
"github.com/ncw/rclone/fs"
"github.com/ncw/rclone/fs/asyncreader"
@ -208,3 +210,46 @@ func TestAccountMaxTransfer(t *testing.T) {
assert.Equal(t, ErrorMaxTransferLimitReached, err)
assert.True(t, fserrors.IsFatalError(err))
}
func TestShortenName(t *testing.T) {
for _, test := range []struct {
in string
size int
want string
}{
{"", 0, ""},
{"abcde", 10, "abcde"},
{"abcde", 0, "abcde"},
{"abcde", -1, "abcde"},
{"abcde", 5, "abcde"},
{"abcde", 4, "ab…e"},
{"abcde", 3, "a…e"},
{"abcde", 2, "a…"},
{"abcde", 1, "…"},
{"abcdef", 6, "abcdef"},
{"abcdef", 5, "ab…ef"},
{"abcdef", 4, "ab…f"},
{"abcdef", 3, "a…f"},
{"abcdef", 2, "a…"},
{"áßcdèf", 1, "…"},
{"áßcdè", 5, "áßcdè"},
{"áßcdè", 4, "áß…è"},
{"áßcdè", 3, "á…è"},
{"áßcdè", 2, "á…"},
{"áßcdè", 1, "…"},
{"áßcdèł", 6, "áßcdèł"},
{"áßcdèł", 5, "áß…èł"},
{"áßcdèł", 4, "áß…ł"},
{"áßcdèł", 3, "á…ł"},
{"áßcdèł", 2, "á…"},
{"áßcdèł", 1, "…"},
} {
t.Run(fmt.Sprintf("in=%q, size=%d", test.in, test.size), func(t *testing.T) {
got := shortenName(test.in, test.size)
assert.Equal(t, test.want, got)
if test.size > 0 {
assert.True(t, utf8.RuneCountInString(got) <= test.size, "too big")
}
})
}
}

View File

@ -92,8 +92,8 @@ type StatsInfo struct {
// NewStats cretates an initialised StatsInfo
func NewStats() *StatsInfo {
return &StatsInfo{
checking: newStringSet(fs.Config.Checkers),
transferring: newStringSet(fs.Config.Transfers),
checking: newStringSet(fs.Config.Checkers, "checking"),
transferring: newStringSet(fs.Config.Transfers, "transferring"),
start: time.Now(),
inProgress: newInProgress(),
}

View File

@ -1,21 +1,26 @@
package accounting
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/ncw/rclone/fs"
)
// stringSet holds a set of strings
type stringSet struct {
mu sync.RWMutex
items map[string]struct{}
name string
}
// newStringSet creates a new empty string set of capacity size
func newStringSet(size int) *stringSet {
func newStringSet(size int, name string) *stringSet {
return &stringSet{
items: make(map[string]struct{}, size),
name: name,
}
}
@ -57,7 +62,11 @@ func (ss *stringSet) Strings() []string {
if acc := Stats.inProgress.get(name); acc != nil {
out = acc.String()
} else {
out = name
out = fmt.Sprintf("%*s: %s",
fs.Config.StatsFileNameLength,
shortenName(name, fs.Config.StatsFileNameLength),
ss.name,
)
}
strings = append(strings, " * "+out)
}

View File

@ -110,7 +110,7 @@ func NewConfig() *ConfigInfo {
c.BufferSize = SizeSuffix(16 << 20)
c.UserAgent = "rclone/" + Version
c.StreamingUploadCutoff = SizeSuffix(100 * 1024)
c.StatsFileNameLength = 40
c.StatsFileNameLength = 45
c.AskPassword = true
c.TPSLimitBurst = 1
c.MaxTransfer = -1