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:
@@ -10,6 +10,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -80,8 +82,10 @@ func main() {
|
|||||||
mux.HandleFunc("GET /synonyms", handleSynonyms(dict))
|
mux.HandleFunc("GET /synonyms", handleSynonyms(dict))
|
||||||
mux.HandleFunc("GET /translate", handleTranslate(dict))
|
mux.HandleFunc("GET /translate", handleTranslate(dict))
|
||||||
mux.HandleFunc("GET /frequency", handleFrequency(dict))
|
mux.HandleFunc("GET /frequency", handleFrequency(dict))
|
||||||
|
mux.HandleFunc("GET /frequency/batch", handleFrequencyBatch(dict))
|
||||||
mux.HandleFunc("GET /health", handleHealth(dict, *dbPath))
|
mux.HandleFunc("GET /health", handleHealth(dict, *dbPath))
|
||||||
|
|
||||||
|
serverStart = time.Now()
|
||||||
addr := fmt.Sprintf("%s:%d", *host, *port)
|
addr := fmt.Sprintf("%s:%d", *host, *port)
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
@@ -119,11 +123,29 @@ func (r *statusRecorder) WriteHeader(code int) {
|
|||||||
r.ResponseWriter.WriteHeader(code)
|
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 {
|
func loggingMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
rec := &statusRecorder{ResponseWriter: w, status: 200}
|
rec := &statusRecorder{ResponseWriter: w, status: 200}
|
||||||
next.ServeHTTP(rec, r)
|
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",
|
slog.Debug("request",
|
||||||
"method", r.Method,
|
"method", r.Method,
|
||||||
"path", r.URL.Path,
|
"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 {
|
func handleHealth(dict *dictionary.Dictionary, dbPath string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
wordCounts, err := dict.WordCount()
|
wordCounts, err := dict.WordCount()
|
||||||
|
|||||||
Reference in New Issue
Block a user