//go:build !js && !wasip1
package main
import (
"fmt"
"log"
"path/filepath"
"time"
"craftdesign.group/quarry/internal/obs"
"craftdesign.group/quarry/internal/obsduck"
)
// openAnalytics wires the durable observation store, returning a stop
// function. Failure is NOT fatal: analytics is derived data about a
// forge that works without it, and a forge that refuses to start
// because its metrics store is unhappy has the priority backwards.
//
// Two files provide this — one behind !js, one behind js — so that the
// wasm build the CGO-free rule exists for never reaches DuckDB. It is
// not enough to leave the import out of the request path: go-duckdb's
// own build constraints exclude wasm, so any reachable import fails the
// build with "build constraints exclude all Go files".
func openAnalytics(dir string, keep time.Duration) func() {
if dir == "" {
// Switched off is a choice, not a fault, and the panel has to
// be able to tell the difference.
obs.Default.SetHealth(obs.Health{})
return func() {}
}
path := filepath.Join(dir, "ops.duckdb")
sink, err := obsduck.Open(path, 0)
if err != nil {
log.Printf("analytics: %v; the live panels still work", err)
obs.Default.SetHealth(obs.Health{Configured: true, Path: path, Err: err.Error()})
return func() {}
}
obs.Default.SetSink(sink)
obs.Default.SetHealth(obs.Health{Configured: true, Path: path, Kept: keptLabel(keep)})
stop := make(chan struct{})
go sink.FlushEvery(30*time.Second, stop)
// Once at startup as well as on the timer: a forge that restarts
// more often than daily would otherwise never reach a prune.
if n, perr := sink.Prune(keep); perr != nil {
log.Printf("analytics: %v", perr)
} else if n > 0 {
log.Printf("analytics: pruned %d observations older than %s", n, keptLabel(keep))
}
go sink.PruneEvery(time.Hour, keep, stop)
go obs.Default.WarmTraffic(stop)
fmt.Println("quarry: analytics ->", path)
return func() {
close(stop)
if err := sink.Close(); err != nil {
log.Printf("analytics: close: %v", err)
}
}
}
// keptLabel names the retention window for the panel and the log.
func keptLabel(keep time.Duration) string {
if keep <= 0 {
return ""
}
if d := keep / (24 * time.Hour); d > 0 && keep%(24*time.Hour) == 0 {
return fmt.Sprintf("%dd", d)
}
return keep.String()
}