github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/progress.go (about) 1 // Show the dynamic progress bar 2 3 package cmd 4 5 import ( 6 "bytes" 7 "fmt" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/rclone/rclone/fs" 13 "github.com/rclone/rclone/fs/accounting" 14 "github.com/rclone/rclone/fs/log" 15 "github.com/rclone/rclone/lib/terminal" 16 ) 17 18 const ( 19 // interval between progress prints 20 defaultProgressInterval = 500 * time.Millisecond 21 // time format for logging 22 logTimeFormat = "2006-01-02 15:04:05" 23 ) 24 25 // startProgress starts the progress bar printing 26 // 27 // It returns a func which should be called to stop the stats. 28 func startProgress() func() { 29 stopStats := make(chan struct{}) 30 oldLogPrint := fs.LogPrint 31 if !log.Redirected() { 32 // Intercept the log calls if not logging to file or syslog 33 fs.LogPrint = func(level fs.LogLevel, text string) { 34 printProgress(fmt.Sprintf("%s %-6s: %s", time.Now().Format(logTimeFormat), level, text)) 35 36 } 37 } 38 var wg sync.WaitGroup 39 wg.Add(1) 40 go func() { 41 defer wg.Done() 42 progressInterval := defaultProgressInterval 43 if ShowStats() && *statsInterval > 0 { 44 progressInterval = *statsInterval 45 } 46 ticker := time.NewTicker(progressInterval) 47 for { 48 select { 49 case <-ticker.C: 50 printProgress("") 51 case <-stopStats: 52 ticker.Stop() 53 printProgress("") 54 fs.LogPrint = oldLogPrint 55 fmt.Println("") 56 return 57 } 58 } 59 }() 60 return func() { 61 close(stopStats) 62 wg.Wait() 63 } 64 } 65 66 // state for the progress printing 67 var ( 68 nlines = 0 // number of lines in the previous stats block 69 progressMu sync.Mutex 70 ) 71 72 // printProgress prints the progress with an optional log 73 func printProgress(logMessage string) { 74 progressMu.Lock() 75 defer progressMu.Unlock() 76 77 var buf bytes.Buffer 78 w, _ := terminal.GetSize() 79 stats := strings.TrimSpace(accounting.GlobalStats().String()) 80 logMessage = strings.TrimSpace(logMessage) 81 82 out := func(s string) { 83 buf.WriteString(s) 84 } 85 86 if logMessage != "" { 87 out("\n") 88 out(terminal.MoveUp) 89 } 90 // Move to the start of the block we wrote erasing all the previous lines 91 for i := 0; i < nlines-1; i++ { 92 out(terminal.EraseLine) 93 out(terminal.MoveUp) 94 } 95 out(terminal.EraseLine) 96 out(terminal.MoveToStartOfLine) 97 if logMessage != "" { 98 out(terminal.EraseLine) 99 out(logMessage + "\n") 100 } 101 fixedLines := strings.Split(stats, "\n") 102 nlines = len(fixedLines) 103 for i, line := range fixedLines { 104 if len(line) > w { 105 line = line[:w] 106 } 107 out(line) 108 if i != nlines-1 { 109 out("\n") 110 } 111 } 112 terminal.Write(buf.Bytes()) 113 }