usage

package
v0.2.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: MPL-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package usage emits one append-only event per completed request — the raw material a future control plane aggregates into per-tenant counts, quotas, and billing. This slice is log-only: no counting, no enforcement, no entitlement state. A request finishes, we record what we already know at the convergence point (rid, tenant, sizes, timing, status), and something downstream derives everything else.

The package is interface-first so the transport is swappable without touching call sites. The default ZapSink writes a structured "usage" line through the chassis's existing zap logger; a later file / Kafka / NATS / OTEL sink drops in behind the same Sink.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Register added in v0.2.4

func Register(name string, c Constructor)

Register adds a sink constructor. Called from a backend package's init(); the chassis activates a backend with a blank import.

Types

type Constructor added in v0.2.4

type Constructor func(SinkConfig) (Sink, error)

Constructor builds a Sink from resolved config. Called by Open.

type Sink

type Sink interface {
	WriteEvent(ev UsageEvent)
	Name() string
	Close(ctx context.Context) error
}

Sink consumes usage events. WriteEvent must be safe for concurrent calls (the bus loop emits from per-request goroutines). Close is called once on chassis shutdown; synchronous sinks return nil immediately. Name reports the backend's registered name for the startup load log.

func Open added in v0.2.4

func Open(name string, cfg SinkConfig) (Sink, error)

Open constructs the named sink. Unknown name is a startup error listing what is available (sorted for a stable message).

type SinkConfig added in v0.2.4

type SinkConfig struct {
	// Epoch is a per-process token that changes every boot — the
	// counter-reset boundary for a sink that keeps cumulative state.
	Epoch string
	// NodeID is a stable identity for this chassis (FQDN-ish), for
	// attribution when many nodes write to one store.
	NodeID string
	// DataDir is where a sink may put a node-local database.
	DataDir string
	// Logger is the chassis logger; a sink may emit observability lines.
	Logger *zap.Logger
}

SinkConfig carries the node/runtime context a sink may need, resolved from chassis config. It is deliberately generic — no billing, quota, or transport vocabulary — so the seam stays unopinionated: a backend reads any backend-specific settings (a DSN, periods) from its own env in its constructor, the same discipline as continuation/trace StoreConfig. The bundled "zap" sink uses only Logger; the other fields are there for an out-of-tree sink that aggregates per node.

type UsageEvent

type UsageEvent struct {
	RID        string
	Tenant     string
	Src        string // "http" | "tcp" | "cron"
	Stack      string // entry stage the request dispatched to
	DurationMS int64
	Status     string // "ok" | "error"
	BytesIn    int
	BytesOut   int
	// MemBytes is peak guest memory for a compute invocation (src="compute").
	// Zero for request-level usage (http/tcp/cron have no wasm memory) and
	// omitted from the log line in that case.
	MemBytes int
	// Fuel is the total per-request fuel consumed across accounted actions
	// (scope-enter, repeat-transition, EXEC, secret-materialize in v1;
	// finer-grained costs follow in v2). Zero on requests that did no
	// accounted work (empty / unrouted), in which case it is omitted from
	// the log line — mirrors the MemBytes conditional pattern. This is the
	// metering primitive; per-request enforcement happens upstream via the
	// chassis budget guards and is invisible to the sink.
	Fuel int64

	// AdmissionDenied marks a request the admission gate rejected (suspend,
	// rate limit, concurrency, or drain) before the customer stack ran. For
	// such requests Billable is false and Fuel is zeroed, so a downstream
	// aggregator never counts rejected traffic as usage. AdmissionReason is
	// the machine token ("rate_limited" | "at_capacity" | "suspended" |
	// "payment_required" | "draining").
	AdmissionDenied bool
	AdmissionReason string
	Billable        bool
}

UsageEvent is the per-request record. Deliberately flat and derived-metric-free: ops counts, monthly rollups, and rate-limit inputs are a downstream consumer's job, not the runtime's.

Tenant is the resolved tenant slug when routing succeeded; it is "_sys" (or empty) for unrouted/404 traffic, which is logged as-is so rejected requests are still measured.

type ZapSink

type ZapSink struct {
	// contains filtered or unexported fields
}

ZapSink is the bundled default sink: it folds the event into a single structured log line. The stable "usage" message is the downstream filter key; the timestamp is zap's own. Close is a no-op — the logger's flush lifecycle is owned by the chassis, not here.

func NewZapSink

func NewZapSink(log *zap.Logger) *ZapSink

NewZapSink wraps an existing chassis logger. The logger is expected to be non-nil; callers only construct a ZapSink when usage is enabled.

func (*ZapSink) Close

func (s *ZapSink) Close(context.Context) error

func (*ZapSink) Name added in v0.2.4

func (s *ZapSink) Name() string

func (*ZapSink) WriteEvent

func (s *ZapSink) WriteEvent(ev UsageEvent)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL