A lightweight and elegant Go package for real-time progress logging with automatic rate tracking, ETA estimation, and human-friendly formatting.
Ideal for command-line tools, data processing pipelines, and long-running batch jobs.
- 🔁 Real-time updates (logged every second)
- ⏱️ Tracks items per second (instantaneous and average)
- 📈 Displays progress, percent complete, elapsed time, and ETA
- 💬 Prints a clear summary when complete
- 🔒 Thread-safe — works cleanly in concurrent contexts
- 🧮 Human-readable numbers (e.g.,
1.2K,3.5M,2.1B) - ⏳ Smart duration formatting (
1m 23s,2h 10m, etc.)
go get github.com/blakesanie/progressThen, import it in your Go project:
import "github.com/blakesanie/progress"package main
import (
"sync/atomic"
"time"
"github.com/blakesanie/progress"
)
func main() {
var total int64 = 10_000
pl := progress.NewProgressLogger("Processing", &total)
for i := 0; i < int(total); i++ {
pl.Add(1)
time.Sleep(100 * time.Microsecond) // simulate work
}
// Optional: explicitly mark done (if total isn't provided or loop ends early)
pl.Done()
}Output (example):
Begin Processing
Processing: 4.8K/10.0K | 952/s | 48.0% | Elapsed 5s | ETA 5s
Processing: 9.9K/10.0K | 1.0K/s | 99.0% | Elapsed 10s | ETA 0s
======================================
Processing: Finished!
Total: 10.0K / 10.0K
Elapsed time: 10s
Average speed: 1.0K/s
======================================
When you know how many units you’ll process, provide a pointer to the total count:
var total int64 = 5_000
pl := progress.NewProgressLogger("Downloading Files", &total)As you process each item:
pl.Add(1)The logger automatically stops when current == total.
If you don’t know the total upfront, you can pass nil for the total:
pl := progress.NewProgressLogger("Streaming", nil)Then, add progress dynamically:
pl.Add(50)When your stream ends:
pl.Done()Example output:
Begin Streaming
Streaming: 50 done | 25/s
Streaming: 100 done | 30/s
======================================
Streaming: Finished!
Total: 100
Elapsed time: 3s
Average speed: 33/s
======================================
ProgressLogger is fully safe for concurrent updates.
You can safely call .Add() from multiple goroutines:
var total int64 = 1000
pl := progress.NewProgressLogger("Concurrent Tasks", &total)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 100; j++ {
pl.Add(1)
time.Sleep(time.Millisecond)
}
}()
}
wg.Wait()
pl.Done()Creates a new progress logger with the given label and optional total count.
Increments progress by n units. If a total is defined and the total is reached, logging automatically stops.
Stops the progress logger manually and prints a final summary. Useful for cases where the total is unknown or the operation ends early.
- Progress is printed every second (
time.NewTicker(time.Second)). - Instantaneous rate (
items/s) is computed from delta changes per tick. - Average speed is computed over the entire elapsed time.
- Formatting functions:
formatNumber: Converts raw counts to readable suffixes (K, M, B, T)formatDuration: Converts seconds into concise time strings.
MIT License © Blake Sanie