Add live status line and frequency batch endpoint

Display query count, error count, req/s, uptime, and last endpoint
hit on a single updating line to stdout. Also wire up the
/frequency/batch route that was missing from the mux.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-03-31 20:01:51 -07:00
parent eb471b6f6d
commit d5e4ce0d36

View File

@@ -10,6 +10,8 @@ import (
"os"
"os/signal"
"strconv"
"strings"
"sync/atomic"
"syscall"
"time"
@@ -80,8 +82,10 @@ func main() {
mux.HandleFunc("GET /synonyms", handleSynonyms(dict))
mux.HandleFunc("GET /translate", handleTranslate(dict))
mux.HandleFunc("GET /frequency", handleFrequency(dict))
mux.HandleFunc("GET /frequency/batch", handleFrequencyBatch(dict))
mux.HandleFunc("GET /health", handleHealth(dict, *dbPath))
serverStart = time.Now()
addr := fmt.Sprintf("%s:%d", *host, *port)
srv := &http.Server{
Addr: addr,
@@ -119,11 +123,29 @@ func (r *statusRecorder) WriteHeader(code int) {
r.ResponseWriter.WriteHeader(code)
}
var (
queryCount atomic.Uint64
errorCount atomic.Uint64
serverStart time.Time
lastPath atomic.Value // stores string
)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rec := &statusRecorder{ResponseWriter: w, status: 200}
next.ServeHTTP(rec, r)
n := queryCount.Add(1)
if rec.status >= 400 {
errorCount.Add(1)
}
lastPath.Store(r.URL.Path)
uptime := time.Since(serverStart).Truncate(time.Second)
rps := float64(n) / time.Since(serverStart).Seconds()
errs := errorCount.Load()
last, _ := lastPath.Load().(string)
fmt.Fprintf(os.Stdout, "\r\033[2KQueries: %d | Errors: %d | Req/s: %.1f | Uptime: %s | Last: %s",
n, errs, rps, uptime, last)
slog.Debug("request",
"method", r.Method,
"path", r.URL.Path,
@@ -327,6 +349,38 @@ func handleFrequency(dict *dictionary.Dictionary) http.HandlerFunc {
}
}
func handleFrequencyBatch(dict *dictionary.Dictionary) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
wordsParam := r.URL.Query().Get("words")
lang := r.URL.Query().Get("lang")
if wordsParam == "" || lang == "" {
writeError(w, 400, "bad_request", "words and lang are required")
return
}
if !dictionary.ValidLang(lang) {
writeError(w, 400, "bad_request", "unsupported language: "+lang)
return
}
words := strings.Split(wordsParam, ",")
if len(words) > 100 {
writeError(w, 400, "bad_request", "maximum 100 words per batch")
return
}
freqs, err := dict.FrequencyBatch(words, lang)
if err != nil {
slog.Error("frequency batch", "error", err)
writeError(w, 500, "internal", "internal error")
return
}
writeJSON(w, 200, map[string]any{
"lang": lang,
"frequencies": freqs,
})
}
}
func handleHealth(dict *dictionary.Dictionary, dbPath string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
wordCounts, err := dict.WordCount()