WithDebugOutput
Vladimir Bauer
8 years ago
| 295 | 295 | } |
| 296 | 296 | } |
| 297 | 297 | |
| 298 | func (b *Bar) render(tw int, pSyncer, aSyncer *widthSyncer) <-chan *renderedState { | |
| 298 | func (b *Bar) render(debugOut io.Writer, tw int, pSyncer, aSyncer *widthSyncer) <-chan *renderedState { | |
| 299 | 299 | ch := make(chan *renderedState, 1) |
| 300 | 300 | |
| 301 | 301 | go func() { |
| 305 | 305 | defer func() { |
| 306 | 306 | // recovering if external decorators panic |
| 307 | 307 | if p := recover(); p != nil { |
| 308 | s.panicMsg = fmt.Sprintf("b#%02d panic: %v\n", s.id, p) | |
| 308 | s.panicMsg = fmt.Sprintf("panic: %v", p) | |
| 309 | 309 | s.pDecorators = nil |
| 310 | 310 | s.aDecorators = nil |
| 311 | 311 | s.toComplete = true |
| 312 | r = strings.NewReader(s.panicMsg) | |
| 312 | // truncate panic msg to one tw line, if necessary | |
| 313 | r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw-1), s.panicMsg)) | |
| 314 | fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg) | |
| 313 | 315 | } |
| 314 | 316 | ch <- &renderedState{b, r, s.toComplete} |
| 315 | 317 | }() |
| 319 | 321 | s := b.cacheState |
| 320 | 322 | var r io.Reader |
| 321 | 323 | if s.panicMsg != "" { |
| 322 | r = strings.NewReader(s.panicMsg) | |
| 324 | r = strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw-1), s.panicMsg)) | |
| 323 | 325 | } else { |
| 324 | 326 | r = s.draw(tw, pSyncer, aSyncer) |
| 325 | 327 | } |
| 90 | 90 | p := New(WithOutput(&buf), WithWaitGroup(&wg)) |
| 91 | 91 | |
| 92 | 92 | wantPanic := "Upps!!!" |
| 93 | numBars := 3 | |
| 93 | numBars := 1 | |
| 94 | 94 | wg.Add(numBars) |
| 95 | 95 | |
| 96 | 96 | for i := 0; i < numBars; i++ { |
| 97 | 97 | name := fmt.Sprintf("b#%02d:", i) |
| 98 | bar := p.AddBar(100, BarID(i), PrependDecorators( | |
| 98 | bar := p.AddBar(100, PrependDecorators( | |
| 99 | 99 | func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { |
| 100 | if s.ID == 2 && s.Current >= 42 { | |
| 100 | if s.Current >= 42 { | |
| 101 | 101 | panic(wantPanic) |
| 102 | 102 | } |
| 103 | 103 | return name |
| 115 | 115 | |
| 116 | 116 | p.Wait() |
| 117 | 117 | |
| 118 | wantPanic = fmt.Sprintf("b#%02d panic: %v", 2, wantPanic) | |
| 118 | wantPanic = fmt.Sprintf("panic: %s", wantPanic) | |
| 119 | lastLine := getLastLine(buf.Bytes()) | |
| 119 | 120 | |
| 120 | if !strings.Contains(buf.String(), wantPanic) { | |
| 121 | if string(lastLine) != wantPanic { | |
| 121 | 122 | t.Errorf("Want: %q, got: %q\n", wantPanic, buf.String()) |
| 122 | 123 | } |
| 123 | 124 | } |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | "os" | |
| 4 | 5 | "sync" |
| 5 | 6 | "time" |
| 6 | 7 | |
| 10 | 11 | |
| 11 | 12 | func main() { |
| 12 | 13 | var wg sync.WaitGroup |
| 13 | p := mpb.New(mpb.WithWaitGroup(&wg)) | |
| 14 | p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr)) | |
| 14 | 15 | |
| 15 | wantPanic := "Upps!!!" | |
| 16 | wantPanic := "Some realy long panic panic panic panic panic panic panic, really it is very long" | |
| 16 | 17 | numBars := 3 |
| 17 | 18 | wg.Add(numBars) |
| 18 | 19 | |
| 78 | 78 | } |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | // WithDebugOutput sets debug output. | |
| 82 | func WithDebugOutput(w io.Writer) ProgressOption { | |
| 83 | return func(s *pState) { | |
| 84 | if w == nil { | |
| 85 | w = ioutil.Discard | |
| 86 | } | |
| 87 | s.debugOut = w | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 81 | 91 | // WithInterceptors provides a way to write to the underlying progress pool's |
| 82 | 92 | // writer. Could be useful if you want to output something below the bars, while |
| 83 | 93 | // they're rendering. |
| 2 | 2 | import ( |
| 3 | 3 | "container/heap" |
| 4 | 4 | "io" |
| 5 | "io/ioutil" | |
| 5 | 6 | "os" |
| 6 | 7 | "sync" |
| 7 | 8 | "time" |
| 46 | 47 | shutdownNotifier chan struct{} |
| 47 | 48 | interceptors []func(io.Writer) |
| 48 | 49 | waitBars map[*Bar]*Bar |
| 50 | debugOut io.Writer | |
| 49 | 51 | } |
| 50 | 52 | widthSyncer struct { |
| 51 | 53 | // Public for easy testing |
| 67 | 69 | rr: prr, |
| 68 | 70 | ticker: time.NewTicker(prr), |
| 69 | 71 | waitBars: make(map[*Bar]*Bar), |
| 72 | debugOut: ioutil.Discard, | |
| 70 | 73 | } |
| 71 | 74 | |
| 72 | 75 | for _, opt := range options { |
| 244 | 247 | for s.bHeap.Len() > 0 { |
| 245 | 248 | b := heap.Pop(s.bHeap).(*Bar) |
| 246 | 249 | defer heap.Push(s.bHeap, b) |
| 247 | pp = append(pp, b.render(tw, pSyncer, aSyncer)) | |
| 250 | pp = append(pp, b.render(s.debugOut, tw, pSyncer, aSyncer)) | |
| 248 | 251 | } |
| 249 | 252 | return pp |
| 250 | 253 | } |
| 42 | 42 | tw, _, _ := cwriter.TermSize() |
| 43 | 43 | err := s.writeAndFlush(tw, numP, numA) |
| 44 | 44 | if err != nil { |
| 45 | fmt.Fprintln(os.Stderr, err) | |
| 45 | fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) | |
| 46 | 46 | } |
| 47 | 47 | case <-winch: |
| 48 | 48 | if s.heapUpdated { |
| 53 | 53 | tw, _, _ := cwriter.TermSize() |
| 54 | 54 | err := s.writeAndFlush(tw-tw/8, numP, numA) |
| 55 | 55 | if err != nil { |
| 56 | fmt.Fprintln(os.Stderr, err) | |
| 56 | fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) | |
| 57 | 57 | } |
| 58 | 58 | if timer != nil && timer.Reset(resumeDelay) { |
| 59 | 59 | break |
| 10 | 10 | |
| 11 | 11 | . "github.com/vbauerster/mpb" |
| 12 | 12 | "github.com/vbauerster/mpb/cwriter" |
| 13 | ) | |
| 14 | ||
| 15 | var ( | |
| 16 | cursorUp = fmt.Sprintf("%c[%dA", cwriter.ESC, 1) | |
| 17 | clearLine = fmt.Sprintf("%c[2K\r", cwriter.ESC) | |
| 18 | clearCursorAndLine = cursorUp + clearLine | |
| 13 | 19 | ) |
| 14 | 20 | |
| 15 | 21 | func init() { |
| 105 | 111 | } |
| 106 | 112 | } |
| 107 | 113 | |
| 108 | var ( | |
| 109 | cursorUp = fmt.Sprintf("%c[%dA", cwriter.ESC, 1) | |
| 110 | clearLine = fmt.Sprintf("%c[2K\r", cwriter.ESC) | |
| 111 | clearCursorAndLine = cursorUp + clearLine | |
| 112 | ) | |
| 113 | ||
| 114 | 114 | func TestWithFormat(t *testing.T) { |
| 115 | 115 | var buf bytes.Buffer |
| 116 | 116 | customFormat := "╢▌▌░╟" |
| 128 | 128 | |
| 129 | 129 | p.Wait() |
| 130 | 130 | |
| 131 | bb := bytes.Split(buf.Bytes(), []byte("\n")) | |
| 132 | lastLine := bb[len(bb)-2] | |
| 133 | lastLine = lastLine[len(clearCursorAndLine):] | |
| 131 | lastLine := getLastLine(buf.Bytes()) | |
| 134 | 132 | |
| 135 | 133 | for _, r := range customFormat { |
| 136 | 134 | if !bytes.ContainsRune(lastLine, r) { |
| 139 | 137 | } |
| 140 | 138 | } |
| 141 | 139 | |
| 140 | func getLastLine(bb []byte) []byte { | |
| 141 | split := bytes.Split(bb, []byte("\n")) | |
| 142 | lastLine := split[len(split)-2] | |
| 143 | return lastLine[len(clearCursorAndLine):] | |
| 144 | } | |
| 145 | ||
| 142 | 146 | func randomDuration(max time.Duration) time.Duration { |
| 143 | 147 | return time.Duration(rand.Intn(10)+1) * max / 10 |
| 144 | 148 | } |
| 3 | 3 | |
| 4 | 4 | import ( |
| 5 | 5 | "fmt" |
| 6 | "os" | |
| 6 | "time" | |
| 7 | 7 | |
| 8 | 8 | "github.com/vbauerster/mpb/cwriter" |
| 9 | 9 | ) |
| 31 | 31 | tw, _, _ := cwriter.TermSize() |
| 32 | 32 | err := s.writeAndFlush(tw, numP, numA) |
| 33 | 33 | if err != nil { |
| 34 | fmt.Fprintln(os.Stderr, err) | |
| 34 | fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err) | |
| 35 | 35 | } |
| 36 | 36 | } |
| 37 | 37 | } |