diff --git a/bar.go b/bar.go index 032a450..f259709 100644 --- a/bar.go +++ b/bar.go @@ -296,7 +296,7 @@ } } -func (b *Bar) render(tw int, pSyncer, aSyncer *widthSyncer) <-chan *renderedState { +func (b *Bar) render(debugOut io.Writer, tw int, pSyncer, aSyncer *widthSyncer) <-chan *renderedState { ch := make(chan *renderedState, 1) go func() { @@ -306,11 +306,13 @@ defer func() { // recovering if external decorators panic if p := recover(); p != nil { - s.panicMsg = fmt.Sprintf("b#%02d panic: %v\n", s.id, p) + s.panicMsg = fmt.Sprintf("panic: %v", p) s.pDecorators = nil s.aDecorators = nil s.toComplete = true - r = strings.NewReader(s.panicMsg) + // truncate panic msg to one tw line, if necessary + r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw-1), s.panicMsg)) + fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg) } ch <- &renderedState{b, r, s.toComplete} }() @@ -320,7 +322,7 @@ s := b.cacheState var r io.Reader if s.panicMsg != "" { - r = strings.NewReader(s.panicMsg) + r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw-1), s.panicMsg)) } else { r = s.draw(tw, pSyncer, aSyncer) } diff --git a/bar_test.go b/bar_test.go index 21c55a2..eb224eb 100644 --- a/bar_test.go +++ b/bar_test.go @@ -91,14 +91,14 @@ p := New(WithOutput(&buf), WithWaitGroup(&wg)) wantPanic := "Upps!!!" - numBars := 3 + numBars := 1 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("b#%02d:", i) - bar := p.AddBar(100, BarID(i), PrependDecorators( + bar := p.AddBar(100, PrependDecorators( func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { - if s.ID == 2 && s.Current >= 42 { + if s.Current >= 42 { panic(wantPanic) } return name @@ -116,9 +116,10 @@ p.Wait() - wantPanic = fmt.Sprintf("b#%02d panic: %v", 2, wantPanic) + wantPanic = fmt.Sprintf("panic: %s", wantPanic) + lastLine := getLastLine(buf.Bytes()) - if !strings.Contains(buf.String(), wantPanic) { + if string(lastLine) != wantPanic { t.Errorf("Want: %q, got: %q\n", wantPanic, buf.String()) } } diff --git a/examples/panic/main.go b/examples/panic/main.go index f4c7b11..10beebc 100644 --- a/examples/panic/main.go +++ b/examples/panic/main.go @@ -2,6 +2,7 @@ import ( "fmt" + "os" "sync" "time" @@ -11,9 +12,9 @@ func main() { var wg sync.WaitGroup - p := mpb.New(mpb.WithWaitGroup(&wg)) + p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr)) - wantPanic := "Upps!!!" + wantPanic := "Some realy long panic panic panic panic panic panic panic, really it is very long" numBars := 3 wg.Add(numBars) diff --git a/options.go b/options.go index 0b0bee4..badfe1f 100644 --- a/options.go +++ b/options.go @@ -79,6 +79,16 @@ } } +// WithDebugOutput sets debug output. +func WithDebugOutput(w io.Writer) ProgressOption { + return func(s *pState) { + if w == nil { + w = ioutil.Discard + } + s.debugOut = w + } +} + // WithInterceptors provides a way to write to the underlying progress pool's // writer. Could be useful if you want to output something below the bars, while // they're rendering. diff --git a/progress.go b/progress.go index f1d3106..081c5ba 100644 --- a/progress.go +++ b/progress.go @@ -3,6 +3,7 @@ import ( "container/heap" "io" + "io/ioutil" "os" "sync" "time" @@ -47,6 +48,7 @@ shutdownNotifier chan struct{} interceptors []func(io.Writer) waitBars map[*Bar]*Bar + debugOut io.Writer } widthSyncer struct { // Public for easy testing @@ -68,6 +70,7 @@ rr: prr, ticker: time.NewTicker(prr), waitBars: make(map[*Bar]*Bar), + debugOut: ioutil.Discard, } for _, opt := range options { @@ -245,7 +248,7 @@ for s.bHeap.Len() > 0 { b := heap.Pop(s.bHeap).(*Bar) defer heap.Push(s.bHeap, b) - pp = append(pp, b.render(tw, pSyncer, aSyncer)) + pp = append(pp, b.render(s.debugOut, tw, pSyncer, aSyncer)) } return pp } diff --git a/progress_posix.go b/progress_posix.go index 56c9cb7..44aa61a 100644 --- a/progress_posix.go +++ b/progress_posix.go @@ -43,7 +43,7 @@ tw, _, _ := cwriter.TermSize() err := s.writeAndFlush(tw, numP, numA) if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) } case <-winch: if s.heapUpdated { @@ -54,7 +54,7 @@ tw, _, _ := cwriter.TermSize() err := s.writeAndFlush(tw-tw/8, numP, numA) if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) } if timer != nil && timer.Reset(resumeDelay) { break diff --git a/progress_test.go b/progress_test.go index 482e519..2f32af3 100644 --- a/progress_test.go +++ b/progress_test.go @@ -11,6 +11,12 @@ . "github.com/vbauerster/mpb" "github.com/vbauerster/mpb/cwriter" +) + +var ( + cursorUp = fmt.Sprintf("%c[%dA", cwriter.ESC, 1) + clearLine = fmt.Sprintf("%c[2K\r", cwriter.ESC) + clearCursorAndLine = cursorUp + clearLine ) func init() { @@ -106,12 +112,6 @@ } } -var ( - cursorUp = fmt.Sprintf("%c[%dA", cwriter.ESC, 1) - clearLine = fmt.Sprintf("%c[2K\r", cwriter.ESC) - clearCursorAndLine = cursorUp + clearLine -) - func TestWithFormat(t *testing.T) { var buf bytes.Buffer customFormat := "╢▌▌░╟" @@ -129,9 +129,7 @@ p.Wait() - bb := bytes.Split(buf.Bytes(), []byte("\n")) - lastLine := bb[len(bb)-2] - lastLine = lastLine[len(clearCursorAndLine):] + lastLine := getLastLine(buf.Bytes()) for _, r := range customFormat { if !bytes.ContainsRune(lastLine, r) { @@ -140,6 +138,12 @@ } } +func getLastLine(bb []byte) []byte { + split := bytes.Split(bb, []byte("\n")) + lastLine := split[len(split)-2] + return lastLine[len(clearCursorAndLine):] +} + func randomDuration(max time.Duration) time.Duration { return time.Duration(rand.Intn(10)+1) * max / 10 } diff --git a/progress_windows.go b/progress_windows.go index 4c4eb6f..9890ee1 100644 --- a/progress_windows.go +++ b/progress_windows.go @@ -4,7 +4,7 @@ import ( "fmt" - "os" + "time" "github.com/vbauerster/mpb/cwriter" ) @@ -32,7 +32,7 @@ tw, _, _ := cwriter.TermSize() err := s.writeAndFlush(tw, numP, numA) if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) } } }