metrics

package
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package metrics records per-endpoint request counts + errors so the dashboard can show at-a-glance health next to each op: how busy it is, whether it's failing, and if so, what the last error was.

The middleware layer lives in middleware.go; this file is the data plane — a Store interface + in-memory implementation. Swap the store for a Prometheus- or StatsD-backed version later without touching the handlers or UI.

Index

Constants

View Source
const RecentErrorsCap = 1000

RecentErrorsCap is the ring-buffer depth per endpoint. Large enough to survive a burst without losing context; /stats polling strips the events so the hot path doesn't pay for this depth — the dashboard fetches the full ring on demand via the per-op errors endpoint.

Variables

This section is empty.

Functions

func NewMiddleware

func NewMiddleware(store Store, key string) middleware.Middleware

NewMiddleware returns a transport-agnostic middleware bundle that counts requests + errors for key. Same bundle serves REST (via Gin) and GraphQL (via Graph); nexus auto-attaches it to every reflective registration so the dashboard populates without extra wiring.

For custom transports (raw gin routes not registered via AsRest, etc.) you can call NewMiddleware directly and thread the Gin realization through c.Next.

Types

type EndpointStats

type EndpointStats struct {
	Key          string       `json:"key"`
	Count        int64        `json:"count"`
	Errors       int64        `json:"errors"`
	LastError    string       `json:"lastError,omitempty"`
	LastAt       time.Time    `json:"lastAt,omitempty"`    // time of last request
	LastErrAt    time.Time    `json:"lastErrAt,omitempty"` // time of last errored request
	RecentErrors []ErrorEvent `json:"recentErrors,omitempty"`
}

EndpointStats is the serializable per-op snapshot the dashboard shows next to each op row on the Architecture tab — request counter, error counter, plus the most recent error's message and timestamp for a "what broke?" quick read.

RecentErrors is a ring-capped list of recent error events (most recent first) so the UI can pop a dialog with timestamps + IPs + messages when an operator clicks the error badge.

type ErrorEvent

type ErrorEvent struct {
	Timestamp time.Time `json:"timestamp"`
	IP        string    `json:"ip,omitempty"`
	Message   string    `json:"message"`
}

ErrorEvent captures one error occurrence for the dashboard's click- through panel. Rolling per-endpoint ring keeps the latest few so operators see both "who's hitting this" (IP) and "what's wrong" (message) without flipping between tabs.

type MemoryStore

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

MemoryStore is the default Store. Per-key entries use atomic ints for counters and a mutex for the last-error text, which mutates rarely compared to the counter path.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore returns an in-process Store. Atomic counters keep the hot path lock-free; a mutex only guards LastError / LastAt updates.

func (*MemoryStore) Errors

func (s *MemoryStore) Errors(key string) []ErrorEvent

Errors returns the ring of recent error events for key, newest first. Empty slice when the key is unknown. Safe to call concurrently with Record — we snapshot under the entry's lock before returning.

func (*MemoryStore) Get

func (s *MemoryStore) Get(key string) (EndpointStats, bool)

func (*MemoryStore) Record

func (s *MemoryStore) Record(key, ip string, err error)

func (*MemoryStore) Snapshot

func (s *MemoryStore) Snapshot() []EndpointStats

type Store

type Store interface {
	// Record one request outcome against key. ip is the caller's client
	// address (empty when unavailable); err is the handler's return
	// (nil on success). Error events get stashed in a per-endpoint ring
	// so the dashboard can surface IP + message together.
	Record(key, ip string, err error)

	// Snapshot returns a point-in-time copy of every key's stats. The
	// returned EndpointStats omits RecentErrors so the /stats polling
	// payload stays small at any ring depth; use Errors(key) for the
	// full per-endpoint error list. Sorted by key.
	Snapshot() []EndpointStats

	// Get returns a single endpoint's stats (without RecentErrors).
	Get(key string) (EndpointStats, bool)

	// Errors returns the ring of recent error events for key, newest
	// first. Called by the dashboard when a user clicks the error
	// badge — lazy fetch means a 1000-entry ring doesn't inflate the
	// hot-polling /stats payload.
	Errors(key string) []ErrorEvent
}

Store is the backend contract. Record is called once per request with nil err on success; Snapshot returns every key's current stats for the dashboard. Safe for concurrent use.

func NewCacheStore

func NewCacheStore(mgr *cache.Manager) Store

NewCacheStore returns a Store backed by a nexus cache.Manager — meaning the in-memory path uses go-cache (the same primitive every other nexus cache does), and the Redis path persists counters so multi-replica deploys see aggregated totals.

Semantics: counters are best-effort. Every Record does a read-modify- write against the cache under the endpoint key, so concurrent writers from different goroutines (or replicas in Redis mode) can step on each other's increments. For a first-pass dashboard view that's fine — if you need exact counts under heavy contention, point your app at a Prometheus collector instead. The API shape stays identical either way so swapping later is a one-line change.

Keys in the cache are namespaced under "nexus.metrics." so they don't collide with whatever else the app caches. TTL is 24h; rolling out restarts within a day preserves counters.

Jump to

Keyboard shortcuts

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