Skip to content
quarry Artifact [d05e8aff5e]
Login

Artifact [d05e8aff5e]

Artifact d05e8aff5e4835070cbaa8a2fe94131eba90cd30c1397a2e73d18a5ebd3d1c6a:


//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(24*time.Hour, keep, 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()
}