Skip to content

Commit

Permalink
progress: method to Log messages while tracking (fixes #195) (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t authored Mar 23, 2022
1 parent afdd730 commit 5d0c7e3
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 18 deletions.
12 changes: 11 additions & 1 deletion cmd/demo-progress/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (

var (
autoStop = flag.Bool("auto-stop", false, "Auto-stop rendering?")
randomFail = flag.Bool("rnd-fail", false, "Enable random failures")
numTrackers = flag.Int("num-trackers", 13, "Number of Trackers")
randomFail = flag.Bool("rnd-fail", false, "Enable random failures in tracking")
randomLogs = flag.Bool("rnd-logs", false, "Enable random logs in the middle of tracking")

messageColors = []text.Color{
text.FgRed,
Expand Down Expand Up @@ -133,7 +134,16 @@ func main() {
// wait for one or more trackers to become active (just blind-wait for a
// second) and then keep watching until Rendering is in progress
time.Sleep(time.Second)
messagesLogged := make(map[string]bool)
for pw.IsRenderInProgress() {
if *randomLogs && pw.LengthDone()%3 == 0 {
logMsg := text.Faint.Sprintf("[INFO] done with %d trackers", pw.LengthDone())
if !messagesLogged[logMsg] {
pw.Log(logMsg)
messagesLogged[logMsg] = true
}
}

// for manual-stop mode, stop when there are no more active trackers
if !*autoStop && pw.LengthActive() == 0 {
pw.Stop()
Expand Down
14 changes: 14 additions & 0 deletions progress/progress.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package progress

import (
"fmt"
"io"
"os"
"sync"
Expand Down Expand Up @@ -28,6 +29,8 @@ type Progress struct {
hideTracker bool
hideValue bool
hidePercentage bool
logsToRender []string
logsToRenderMutex sync.RWMutex
messageWidth int
numTrackersExpected int64
overallTracker *Tracker
Expand Down Expand Up @@ -144,6 +147,17 @@ func (p *Progress) LengthInQueue() int {
return out
}

// Log appends a log to display above the active progress bars during the next
// refresh.
func (p *Progress) Log(msg string, a ...interface{}) {
if len(a) > 0 {
msg = fmt.Sprintf(msg, a...)
}
p.logsToRenderMutex.Lock()
p.logsToRender = append(p.logsToRender, msg)
p.logsToRenderMutex.Unlock()
}

// SetAutoStop toggles the auto-stop functionality. Auto-stop set to true would
// mean that the Render() function will automatically stop once all currently
// active Trackers reach their final states. When set to false, the client code
Expand Down
8 changes: 8 additions & 0 deletions progress/progress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ func TestProgress_LengthInQueue(t *testing.T) {
assert.Equal(t, 1, p.LengthInQueue())
}

func TestProgress_Log(t *testing.T) {
p := Progress{}
assert.Len(t, p.logsToRender, 0)

p.Log("testing log")
assert.Len(t, p.logsToRender, 1)
}

func TestProgress_SetAutoStop(t *testing.T) {
p := Progress{}
assert.False(t, p.autoStop)
Expand Down
49 changes: 32 additions & 17 deletions progress/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,39 +272,54 @@ func (p *Progress) renderTrackers(lastRenderLength int) int {
p.moveCursorToTheTop(&out)
}

// render the trackers that are done, and then the ones that are active
p.renderTrackersDoneAndActive(&out)

// render the overall tracker
if p.showOverallTracker {
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
}

// write the text to the output writer
_, _ = p.outputWriter.Write([]byte(out.String()))

// stop if auto stop is enabled and there are no more active trackers
if p.autoStop && p.LengthActive() == 0 {
p.done <- true
}

return out.Len()
}

func (p *Progress) renderTrackersDoneAndActive(out *strings.Builder) {
// find the currently "active" and "done" trackers
trackersActive, trackersDone := p.extractDoneAndActiveTrackers()

// sort and render the done trackers
for _, tracker := range trackersDone {
p.renderTracker(&out, tracker, renderHint{})
p.renderTracker(out, tracker, renderHint{})
}
p.trackersDoneMutex.Lock()
p.trackersDone = append(p.trackersDone, trackersDone...)
p.trackersDoneMutex.Unlock()

// render all the logs received and flush them out
p.logsToRenderMutex.Lock()
for _, log := range p.logsToRender {
out.WriteString(text.EraseLine.Sprint())
out.WriteString(log)
out.WriteRune('\n')
}
p.logsToRender = nil
p.logsToRenderMutex.Unlock()

// sort and render the active trackers
for _, tracker := range trackersActive {
p.renderTracker(&out, tracker, renderHint{})
p.renderTracker(out, tracker, renderHint{})
}
p.trackersActiveMutex.Lock()
p.trackersActive = trackersActive
p.trackersActiveMutex.Unlock()

// render the overall tracker
if p.showOverallTracker {
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
}

// write the text to the output writer
_, _ = p.outputWriter.Write([]byte(out.String()))

// stop if auto stop is enabled and there are no more active trackers
if p.autoStop && p.LengthActive() == 0 {
p.done <- true
}

return out.Len()
}

func (p *Progress) renderTrackerStats(out *strings.Builder, t *Tracker, hint renderHint) {
Expand Down
4 changes: 4 additions & 0 deletions progress/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
pw.ShowOverallTracker(true)
pw.Style().Options.TimeOverallPrecision = time.Millisecond
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go func() {
pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
}()
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)
Expand All @@ -623,6 +626,7 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+; ~ETA: [\d.ms]+`),
regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
}
out := renderOutput.String()
for _, expectedOutPattern := range expectedOutPatterns {
Expand Down
1 change: 1 addition & 0 deletions progress/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Writer interface {
LengthActive() int
LengthDone() int
LengthInQueue() int
Log(msg string, a ...interface{})
SetAutoStop(autoStop bool)
SetMessageWidth(width int)
SetNumTrackersExpected(numTrackers int)
Expand Down

0 comments on commit 5d0c7e3

Please sign in to comment.