Midnight at the server room
A payments service in Kathmandu writes thousands of log lines an hour: INFO user logged in, ERROR payment failed, and the occasional garbled line. The on-call engineer wants one summary line — how many errors, warnings and info messages, and how many lines were unreadable — and wants it fast.
This capstone pulls the whole course together: strings and slices to parse, a struct for entries, sentinel and wrapped errors for bad lines, a pool of worker goroutines fed by a channel, a mutex-protected map for counts, and a sorted report. It's a miniature version of tools Go is famous for building.
The worker pool
A worker pool starts a fixed number of goroutines that all receive from one jobs channel. The producer sends every job and then closes the channel; each worker's for job := range jobs loop ends when the channel is closed and drained. A WaitGroup tells you when all workers have finished, and a mutex guards the shared results.
jobs := make(chan string)
var wg sync.WaitGroup
for w := 0; w < workers; w++ {
wg.Add(1)
go func() {
defer wg.Done()
for line := range jobs {
// parse, then lock and count
}
}()
}
for _, line := range lines {
jobs <- line
}
close(jobs)
wg.Wait()Why a pool instead of one goroutine per line? Goroutines are cheap, but the work they share often isn't: a fixed pool limits how many files, connections or CPU cores you use at once (mistake #59: the right number depends on whether the work is CPU-bound or I/O-bound).