Count paths in a map, copy into []KV, sort by count descending with cmp.Compare, truncate to n. Scanner buffer raised to 1MB for long lines; malformed lines (<3 fields) are skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012MpK9niZm4jcs3mAJbxMfi
87 lines
2.0 KiB
Go
87 lines
2.0 KiB
Go
// Exercise 04 — Log Crunching: Top-N Endpoints
|
|
//
|
|
// ASSIGNMENT
|
|
// ----------
|
|
// Log lines look like:
|
|
//
|
|
// 2026-08-28T10:00:01Z GET /api/users 200 123ms
|
|
//
|
|
// Implement TopN(r, n) so that it:
|
|
//
|
|
// 1. Streams the input (must handle logs far larger than memory — no
|
|
// io.ReadAll).
|
|
// 2. Counts requests per endpoint (3rd whitespace-separated field).
|
|
// 3. Skips malformed lines (fewer than 3 fields) without failing.
|
|
// 4. Handles lines longer than 64KB (hint: bufio.Scanner's default limit).
|
|
// 5. Returns the n most frequent endpoints, most frequent first.
|
|
// Ties: order among equal counts is unspecified.
|
|
//
|
|
// Run the tests: go test -race ./04-log-topn/
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"cmp"
|
|
"fmt"
|
|
"io"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
type KV struct {
|
|
Endpoint string
|
|
Count int
|
|
}
|
|
|
|
// TopN returns the n most frequent endpoints in the log stream.
|
|
// TODO: implement me.
|
|
func TopN(r io.Reader, n int) ([]KV, error) {
|
|
// TODO:
|
|
// - bufio.Scanner with an enlarged buffer
|
|
// - map[string]int for counting
|
|
// - copy map into []KV and sort by count desc (maps can't be sorted)
|
|
// - truncate to n
|
|
|
|
m := make(map[string]int)
|
|
sc := bufio.NewScanner(r)
|
|
sc.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
|
for sc.Scan() {
|
|
//fmt.Println(sc.Text())
|
|
fields := strings.Fields(sc.Text())
|
|
//fmt.Printf("len: %v, fields: %v\n", len(fields), fields)
|
|
if len(fields) < 3 {
|
|
continue
|
|
}
|
|
m[fields[2]]++
|
|
}
|
|
|
|
// copy over map into list of KV
|
|
l := make([]KV, len(m))
|
|
i := 0
|
|
for k, v := range m {
|
|
// println(i, k, v, len(l), len(m))
|
|
l[i] = KV{k, v}
|
|
i++
|
|
}
|
|
|
|
// sort by count desc
|
|
slices.SortFunc(l, func(a, b KV) int {
|
|
return cmp.Compare(b.Count, a.Count)
|
|
})
|
|
if len(l) > n {
|
|
l = l[:n]
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
func main() {
|
|
logs := `2026-08-28T10:00:01Z GET /api/users 200 12ms
|
|
2026-08-28T10:00:02Z GET /api/orders 200 40ms
|
|
2026-08-28T10:00:03Z GET /api/users 500 3ms
|
|
garbage line
|
|
2026-08-28T10:00:04Z POST /api/users 201 88ms`
|
|
top, err := TopN(strings.NewReader(logs), 2)
|
|
fmt.Println(top, err)
|
|
}
|