vbauerster / mpb

multi progress bar for Go cli applications
The Unlicense
2.27k stars 123 forks source link

progressAsWriter with logrus does not work #141

Open Blogoslov opened 3 months ago

Blogoslov commented 3 months ago

Hello. I have took this code from example and substitite standard logger to logrus but if failes with message:

Failed to write to log, mpb.Progress instance can't be reused after mpb.Progress.Wait()

package main

import (
    "fmt"
    "github.com/sirupsen/logrus"
    "math/rand"
    "sync"
    "time"

    "github.com/vbauerster/mpb/v8"
    "github.com/vbauerster/mpb/v8/decor"
)

var logger *logrus.Logger

func main() {
    log := logrus.New()
    total, numBars := 100, 2
    var wg sync.WaitGroup
    wg.Add(numBars)
    done := make(chan interface{})
    p := mpb.New(
        mpb.WithWidth(64),
        mpb.WithWaitGroup(&wg),
        mpb.WithShutdownNotifier(done),
    )

    log.SetOutput(p)

    for i := 0; i < numBars; i++ {
        name := fmt.Sprintf("Bar#%d:", i)
        bar := p.AddBar(int64(total),
            mpb.PrependDecorators(
                decor.Name(name),
                decor.Percentage(decor.WCSyncSpace),
            ),
            mpb.AppendDecorators(
                decor.OnComplete(
                    decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
                ),
            ),
        )
        // simulating some work
        go func() {
            defer wg.Done()
            rng := rand.New(rand.NewSource(time.Now().UnixNano()))
            max := 100 * time.Millisecond
            for i := 0; i < total; i++ {
                // start variable is solely for EWMA calculation
                // EWMA's unit of measure is an iteration's duration
                start := time.Now()
                time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
                // we need to call EwmaIncrement to fulfill ewma decorator's contract
                bar.EwmaIncrement(time.Since(start))
            }
            log.Println(name, "done")
        }()
    }

    var qwg sync.WaitGroup
    qwg.Add(1)
    go func() {
    quit:
        for {
            select {
            case <-done:
                // after done, underlying io.Writer returns mpb.DoneError
                // so following isn't printed
                log.Println("all done")
                break quit
            default:
                log.Println("waiting for done")
                time.Sleep(100 * time.Millisecond)
            }
        }
        qwg.Done()
    }()

    p.Wait()
    qwg.Wait()
}

could you please suggest how to fix this?

vbauerster commented 3 months ago

Given that log's underlying io.Writer is *mpb.Progress, fix would be: don't call log after (*mpb.Progress).Wait(). To understand why ask yourself a question: what a logger should do when underlying io.Writer returns an error?

Blogoslov commented 3 months ago

Sorry, but if I substitite "github.com/sirupsen/logrus" by builtin "log" package (as it was in your original example) - then I don't see such error message and this is why I opened the ticket. I expected that behaviour should be the same. Or not?

Could you please advise how to restore logger after p.Wait? I changed it by commpad log.SetOutput(p) and in my case the program is continue after p.Wait statement and do some other things.