Skip to content

Commit 5d0c7e3

Browse files
authored
progress: method to Log messages while tracking (fixes #195) (#197)
1 parent afdd730 commit 5d0c7e3

File tree

6 files changed

+70
-18
lines changed

6 files changed

+70
-18
lines changed

cmd/demo-progress/demo.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212

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

1819
messageColors = []text.Color{
1920
text.FgRed,
@@ -133,7 +134,16 @@ func main() {
133134
// wait for one or more trackers to become active (just blind-wait for a
134135
// second) and then keep watching until Rendering is in progress
135136
time.Sleep(time.Second)
137+
messagesLogged := make(map[string]bool)
136138
for pw.IsRenderInProgress() {
139+
if *randomLogs && pw.LengthDone()%3 == 0 {
140+
logMsg := text.Faint.Sprintf("[INFO] done with %d trackers", pw.LengthDone())
141+
if !messagesLogged[logMsg] {
142+
pw.Log(logMsg)
143+
messagesLogged[logMsg] = true
144+
}
145+
}
146+
137147
// for manual-stop mode, stop when there are no more active trackers
138148
if !*autoStop && pw.LengthActive() == 0 {
139149
pw.Stop()

progress/progress.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package progress
22

33
import (
4+
"fmt"
45
"io"
56
"os"
67
"sync"
@@ -28,6 +29,8 @@ type Progress struct {
2829
hideTracker bool
2930
hideValue bool
3031
hidePercentage bool
32+
logsToRender []string
33+
logsToRenderMutex sync.RWMutex
3134
messageWidth int
3235
numTrackersExpected int64
3336
overallTracker *Tracker
@@ -144,6 +147,17 @@ func (p *Progress) LengthInQueue() int {
144147
return out
145148
}
146149

150+
// Log appends a log to display above the active progress bars during the next
151+
// refresh.
152+
func (p *Progress) Log(msg string, a ...interface{}) {
153+
if len(a) > 0 {
154+
msg = fmt.Sprintf(msg, a...)
155+
}
156+
p.logsToRenderMutex.Lock()
157+
p.logsToRender = append(p.logsToRender, msg)
158+
p.logsToRenderMutex.Unlock()
159+
}
160+
147161
// SetAutoStop toggles the auto-stop functionality. Auto-stop set to true would
148162
// mean that the Render() function will automatically stop once all currently
149163
// active Trackers reach their final states. When set to false, the client code

progress/progress_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ func TestProgress_LengthInQueue(t *testing.T) {
8787
assert.Equal(t, 1, p.LengthInQueue())
8888
}
8989

90+
func TestProgress_Log(t *testing.T) {
91+
p := Progress{}
92+
assert.Len(t, p.logsToRender, 0)
93+
94+
p.Log("testing log")
95+
assert.Len(t, p.logsToRender, 1)
96+
}
97+
9098
func TestProgress_SetAutoStop(t *testing.T) {
9199
p := Progress{}
92100
assert.False(t, p.autoStop)

progress/render.go

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,39 +272,54 @@ func (p *Progress) renderTrackers(lastRenderLength int) int {
272272
p.moveCursorToTheTop(&out)
273273
}
274274

275+
// render the trackers that are done, and then the ones that are active
276+
p.renderTrackersDoneAndActive(&out)
277+
278+
// render the overall tracker
279+
if p.showOverallTracker {
280+
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
281+
}
282+
283+
// write the text to the output writer
284+
_, _ = p.outputWriter.Write([]byte(out.String()))
285+
286+
// stop if auto stop is enabled and there are no more active trackers
287+
if p.autoStop && p.LengthActive() == 0 {
288+
p.done <- true
289+
}
290+
291+
return out.Len()
292+
}
293+
294+
func (p *Progress) renderTrackersDoneAndActive(out *strings.Builder) {
275295
// find the currently "active" and "done" trackers
276296
trackersActive, trackersDone := p.extractDoneAndActiveTrackers()
277297

278298
// sort and render the done trackers
279299
for _, tracker := range trackersDone {
280-
p.renderTracker(&out, tracker, renderHint{})
300+
p.renderTracker(out, tracker, renderHint{})
281301
}
282302
p.trackersDoneMutex.Lock()
283303
p.trackersDone = append(p.trackersDone, trackersDone...)
284304
p.trackersDoneMutex.Unlock()
285305

306+
// render all the logs received and flush them out
307+
p.logsToRenderMutex.Lock()
308+
for _, log := range p.logsToRender {
309+
out.WriteString(text.EraseLine.Sprint())
310+
out.WriteString(log)
311+
out.WriteRune('\n')
312+
}
313+
p.logsToRender = nil
314+
p.logsToRenderMutex.Unlock()
315+
286316
// sort and render the active trackers
287317
for _, tracker := range trackersActive {
288-
p.renderTracker(&out, tracker, renderHint{})
318+
p.renderTracker(out, tracker, renderHint{})
289319
}
290320
p.trackersActiveMutex.Lock()
291321
p.trackersActive = trackersActive
292322
p.trackersActiveMutex.Unlock()
293-
294-
// render the overall tracker
295-
if p.showOverallTracker {
296-
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
297-
}
298-
299-
// write the text to the output writer
300-
_, _ = p.outputWriter.Write([]byte(out.String()))
301-
302-
// stop if auto stop is enabled and there are no more active trackers
303-
if p.autoStop && p.LengthActive() == 0 {
304-
p.done <- true
305-
}
306-
307-
return out.Len()
308323
}
309324

310325
func (p *Progress) renderTrackerStats(out *strings.Builder, t *Tracker, hint renderHint) {

progress/render_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,9 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
611611
pw.ShowOverallTracker(true)
612612
pw.Style().Options.TimeOverallPrecision = time.Millisecond
613613
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
614+
go func() {
615+
pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
616+
}()
614617
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
615618
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
616619
renderAndWait(pw, false)
@@ -623,6 +626,7 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
623626
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
624627
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
625628
regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+; ~ETA: [\d.ms]+`),
629+
regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
626630
}
627631
out := renderOutput.String()
628632
for _, expectedOutPattern := range expectedOutPatterns {

progress/writer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Writer interface {
1515
LengthActive() int
1616
LengthDone() int
1717
LengthInQueue() int
18+
Log(msg string, a ...interface{})
1819
SetAutoStop(autoStop bool)
1920
SetMessageWidth(width int)
2021
SetNumTrackersExpected(numTrackers int)

0 commit comments

Comments
 (0)