Files
kandev/apps/backend/internal/task/usage/metrics.go
T
Henry 4c0d92154b feat(backend): add per-run cost/token ledger for task usage (#2960)
Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-08-23 22:15:19 +00:00

69 lines
2.2 KiB
Go

package usage
import (
"expvar"
"strings"
"go.uber.org/zap"
)
// expvar maps published at package init, exposed via stdlib's /debug/vars
// handler (dev mode). Mirrors internal/office/service/cost_metrics.go's
// shape for the same reason: this ledger's writer has no other observable
// failure surface between "an event arrived on the bus" and "a row exists",
// so every terminal outcome bumps exactly one of these (AC-27).
var (
eventsWrittenTotal = expvar.NewMap("task_usage_events_written_total")
eventsDroppedTotal = expvar.NewMap("task_usage_events_dropped_total")
)
// Reasons a session-prompt-usage event never became a stored
// task_usage_events row (AC-27). Every terminal state-machine transition
// increments exactly one of these, via whichever of the fixed
// decode/admit/validate/ownership/insert stages the event fails first.
const (
dropReasonDecodeError = "decode_error"
dropReasonUnattributable = "unattributable"
dropReasonInvalid = "invalid"
dropReasonDuplicate = "duplicate"
dropReasonOverflow = "overflow"
dropReasonDrainTimeout = "drain_timeout"
dropReasonShutdown = "shutdown"
dropReasonError = "error"
)
const (
metricEventWritten = "task_usage.metric.event_written"
metricEventDropped = "task_usage.metric.event_dropped"
)
func usageMetricLabel(pairs ...string) string {
if len(pairs)%2 != 0 {
return ""
}
parts := make([]string, 0, len(pairs)/2)
for i := 0; i < len(pairs); i += 2 {
parts = append(parts, pairs[i]+"="+pairs[i+1])
}
return strings.Join(parts, ";")
}
// recordWritten bumps task_usage_events_written_total, keyed by the row's
// resolved cost source and provider.
func (w *Writer) recordWritten(source, provider string) {
eventsWrittenTotal.Add(usageMetricLabel("source", source, "provider", provider), 1)
if w.log == nil {
return
}
w.log.Debug(metricEventWritten, zap.String("cost_source", source), zap.String("provider", provider))
}
// recordDropped bumps task_usage_events_dropped_total, keyed by reason.
func (w *Writer) recordDropped(reason, taskID string) {
eventsDroppedTotal.Add(usageMetricLabel("reason", reason), 1)
if w.log == nil {
return
}
w.log.Warn(metricEventDropped, zap.String("reason", reason), zap.String("task_id", taskID))
}