Documentation
¶
Overview ¶
Package metrics provides a generic timeseries store for recording and querying (timestamp, value) points behind a thin Timeseries interface.
The root package ships an in-process implementation (NewMemory) with no external dependencies, suitable for CLIs, tests, and single-process servers. A cross-process, persistent implementation backed by valkey/redis sorted sets lives in the sibling submodule github.com/flanksource/clicky/valkey, which depends on valkey-go so the root module stays dependency-free.
Both implementations share the same wire format (see codec.go) so a metric recorded by one can be queried by the other, and RegisterRoutes serves either over HTTP without knowing which backend it holds.
Index ¶
- func EncodeMember(p Point) string
- func Handler(ts Timeseries, prefix string) http.Handler
- func RegisterRoutes(router *route.Router, ts Timeseries, prefix string)
- func RegisterStreamRoutes(router *route.Router, src SeriesSource, prefix string)
- type MemoryConfig
- type Point
- type QueryRequest
- type RecordRequest
- type Series
- type SeriesRequest
- type SeriesSource
- type Sources
- type StoreConfig
- type StreamFrame
- type Timeseries
- type UnknownSeriesError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EncodeMember ¶
EncodeMember renders a Point as a sorted-set member string of the form "<unixMillis>:<value>". The millisecond timestamp prefix doubles as the sorted-set score, and including it in the member keeps members unique even when two observations share a value. The value is formatted with -1 precision so it round-trips losslessly through ParseMember.
func Handler ¶
func Handler(ts Timeseries, prefix string) http.Handler
Handler returns the metrics endpoint as a standalone http.Handler for callers that compose their own mux. It routes through the same declaration as RegisterRoutes, so a request is observed identically either way. It expects to be mounted such that request paths look like {prefix}/metrics/{id}.
func RegisterRoutes ¶
func RegisterRoutes(router *route.Router, ts Timeseries, prefix string)
RegisterRoutes mounts the metrics read endpoint on router under prefix:
GET {prefix}/metrics/{id}?since=&until=
The handler owns all request parsing, querying, and JSON encoding — callers supply only a Timeseries. prefix is the leading path segment shared with the rest of the API (e.g. "/api/v1"); it is used verbatim, so pass it without a trailing slash.
func RegisterStreamRoutes ¶ added in v1.21.90
func RegisterStreamRoutes(router *route.Router, src SeriesSource, prefix string)
RegisterStreamRoutes mounts the batched metrics stream on router:
GET {prefix}/metrics/stream?id=a&id=b&since=15m&until=&step=&interval=5s
One subscription serves every id a page of panels shows: each interval it queries src once for all of them and sends an "append" frame (a "snapshot" first), or a ping when nothing changed. A query failure is sent as an "error" event {"error": msg} and ends the stream. Subscribe through the sse Hub (clicky-ui createEventHub) so a tab holds one connection for all streams. since/until take what GET {prefix}/metrics/{id} takes and are re-resolved every tick, so a duration window slides.
Types ¶
type MemoryConfig ¶
MemoryConfig tunes the in-process Timeseries. Zero fields fall back to defaults: Retention 1h, MaxPoints 4096.
type Point ¶
Point is a single observation in a timeseries.
func ParseMember ¶
ParseMember reverses EncodeMember. It returns an error for any member that does not match the "<unixMillis>:<value>" shape so a corrupt entry surfaces loudly rather than decoding to a zero Point.
type QueryRequest ¶
QueryRequest reads the points for metric ID whose timestamps fall in [Since, Until]. The HTTP handler resolves zero bounds to a default window (Until = now, Since = now-1h); implementations treat a zero bound as unbounded on that side.
type RecordRequest ¶
RecordRequest records one Point for metric ID. A zero At is resolved to the current time by the implementation.
type Series ¶ added in v1.21.90
type Series struct {
// ID is unique within a response: the requested id itself, or the
// requested id plus "#" and the canonical label set when one requested id
// expands to several series (a query profile grouped by pod, say).
ID string `json:"id"`
// Source is the id the panel asked for, so a panel can find every series a
// request of its expanded into.
Source string `json:"source"`
Label string `json:"label,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Unit is a clicky-ui formatUnit key (percent, bytes, ms, …).
Unit string `json:"unit,omitempty"`
Points []Point `json:"points"`
}
Series is one plottable line: the points of a single label set, ready for a clicky-ui metrics panel without any client-side reshaping.
type SeriesRequest ¶ added in v1.21.90
SeriesRequest reads every series of IDs in [Since, Until]. Step is the resolution a computing source (Prometheus) evaluates at; a stored source returns its raw points and ignores it.
type SeriesSource ¶ added in v1.21.90
type SeriesSource interface {
QuerySeries(ctx context.Context, req SeriesRequest) ([]Series, error)
}
SeriesSource answers many ids in one call, so a page of panels costs one query per tick rather than one per series.
func StoreSource ¶ added in v1.21.90
func StoreSource(ts Timeseries) SeriesSource
StoreSource serves a Timeseries store as a SeriesSource: one series per id, labelled by nothing but its id.
type Sources ¶ added in v1.21.90
type Sources map[string]SeriesSource
Sources routes each id to the source registered for its prefix — the text before the first ":" (e.g. "profile:pod-cpu?namespace=x"). Ids whose prefix is not registered go to the "" source; store ids such as task metrics ("<runID>:cpu") contain colons, so an unregistered prefix cannot be told apart from a plain id. With no "" source such an id is an UnknownSeriesError.
func (Sources) QuerySeries ¶ added in v1.21.90
type StoreConfig ¶ added in v1.21.34
type StoreConfig struct {
// KeyPrefix is prepended to every key so metrics share the caller's
// namespace. The full key is KeyPrefix + "metric:" + id.
KeyPrefix string
// Retention drops points older than this on each Record. Zero -> 7d.
Retention time.Duration
// MaxPoints caps the points kept per metric ID, dropping the oldest on
// overflow. Zero leaves the series uncapped (time-based retention only).
MaxPoints int
}
StoreConfig tunes a cache-backed Timeseries.
type StreamFrame ¶ added in v1.21.90
type StreamFrame struct {
Series []Series `json:"series"`
Removed []string `json:"removed,omitempty"`
}
StreamFrame is the payload of the stream's "snapshot" and "append" events. A snapshot carries every series in full; an append carries only the series with points newer than the last frame (just those points) plus the ids of series that no longer exist. Clients trim points older than their window.
type Timeseries ¶
type Timeseries interface {
// Record appends a point. Recording is best-effort instrumentation: an
// implementation backed by an unavailable store may return an error, but
// callers are expected to log-and-continue rather than fail the producer.
Record(req RecordRequest) error
// Query returns the points in the requested range, ascending by time.
Query(req QueryRequest) ([]Point, error)
}
Timeseries stores and retrieves timeseries points. Implementations must be safe for concurrent use.
func NewMemory ¶
func NewMemory(cfg MemoryConfig) Timeseries
NewMemory returns an in-process Timeseries. It needs no backend and is the zero-config default for CLIs, tests, and single-process servers. The series is bounded (MaxPoints) so a long-running process cannot grow it without limit.
func NewStore ¶ added in v1.21.34
func NewStore(kv cache.Store, cfg StoreConfig) Timeseries
NewStore returns a Timeseries backed by kv: one sorted set per metric ID, each point a "<unixMillis>:<value>" member scored by its millisecond timestamp (see EncodeMember). Pass valkey.NewStore for a cross-process, persistent series. The kv backend is owned by the caller.
type UnknownSeriesError ¶ added in v1.21.90
type UnknownSeriesError struct{ ID string }
UnknownSeriesError reports an id no registered source owns.
func (*UnknownSeriesError) Error ¶ added in v1.21.90
func (e *UnknownSeriesError) Error() string