Documentation
¶
Overview ¶
Package metricsx provides business-facing metrics for Aisphere Kernel.
metricsx is the recommended package for registering and recording metrics (counters, up-down counters, histograms, gauges) in business code. It wraps go.opentelemetry.io/otel/metric with a simpler API and built-in safety:
- Nil-safe: all methods on a nil Manager are no-ops
- Low-cardinality label validation (warns on > 20 labels)
- Standard system metrics registration helpers (goroutines, memory)
- Prometheus exporter one-liner via NewPrometheusManager
- Logger type reuses logx.Logger
Design principle ¶
metricsx only DEFINES metric registration and recording. It does NOT do logging (use logx), NOT do tracing (use contrib/otel/tracing), NOT do transport middleware (use contrib/otel/metrics for that). It only manages metric instruments.
metricsx depends on logx, go.opentelemetry.io/otel/metric, the Prometheus exporter, and the Go standard library. It does NOT import errorx or transport.
30-second quickstart ¶
// Bootstrap: create a Prometheus-backed manager
m := metricsx.NewPrometheusManager("aihub", "v1.0.0", logger)
// Register metrics at startup
m.NewCounter("skill_create_total", "Total skills created")
m.NewHistogram("skill_query_seconds", "Skill query latency", 0.01, 0.1, 1, 10)
m.NewGauge("active_sessions", "Active sessions")
// Record in business code
m.IncrementCounter(ctx, "skill_create_total", "tenant", "t_acme")
m.RecordHistogram(ctx, "skill_query_seconds", 0.042, "operation", "find")
m.SetGauge("active_sessions", 42, "tenant", "t_acme")
// Expose /metrics endpoint
http.Handle("/metrics", metricsx.GetHandler(m))
Instrument types ¶
m.NewCounter(name, desc) // monotonic, int64 m.NewUpDownCounter(name, desc) // can go up or down, float64 m.NewHistogram(name, desc, buckets...) // distribution, float64 m.NewGauge(name, desc) // current value, float64
Recording ¶
m.IncrementCounter(ctx, name, labels...) // +1 m.DeltaUpDownCounter(ctx, name, value, labels...) // +/- value m.RecordHistogram(ctx, name, value, labels...) // observe value m.SetGauge(name, value, labels...) // set current value
Labels are key-value pairs: "key1", "value1", "key2", "value2".
nil safety ¶
All methods on a nil Manager are no-ops. This means you can pass a nil Manager to test code or disabled features without nil-checks everywhere:
var m metricsx.Manager // nil m.IncrementCounter(ctx, "x") // no-op, no panic
Further reading ¶
See metricsx/README.md for the single-source-of-truth user guide, and docs/ai/metricsx.md for the AI coding recipe.
Example (BusinessBootstrap) ¶
Example_businessBootstrap shows the standard startup pattern: create a Prometheus-backed Manager, register all metrics, expose /metrics endpoint.
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// In real code (cannot run here due to Prometheus registry):
// m := metricsx.NewPrometheusManager("aihub", "v1.0.0", logger)
// metricsx.RegisterSystemMetrics(m)
// m.NewCounter("skill_create_total", "Total skills created")
// m.NewHistogram("skill_query_seconds", "Skill query latency", metricsx.DefaultBuckets...)
// m.NewGauge("active_sessions", "Active sessions")
// http.Handle("/metrics", metricsx.GetHandler(m))
m := metricsx.Noop() // stand-in for example
m.NewCounter("skill_create_total", "Total skills created")
m.NewHistogram("skill_query_seconds", "Skill query latency", metricsx.DefaultBuckets...)
m.NewGauge("active_sessions", "Active sessions")
fmt.Println("ok")
}
Output: ok
Example (BusinessCardinalityWarning) ¶
Example_businessCardinalityWarning shows that metricsx warns when too many labels are passed (potential Prometheus cardinality explosion).
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewCounter("high_cardinality_test", "test")
// 22 labels — triggers a warning log (but still records)
m.IncrementCounter(context.Background(), "high_cardinality_test",
"k1", "v1", "k2", "v2", "k3", "v3", "k4", "v4", "k5", "v5",
"k6", "v6", "k7", "v7", "k8", "v8", "k9", "v9", "k10", "v10",
"k11", "v11",
)
fmt.Println("ok")
}
Output: ok
Example (BusinessCounter) ¶
Example_businessCounter shows counting business events with labels.
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewCounter("skill_create_total", "Total skills created")
m.NewCounter("skill_delete_total", "Total skills deleted")
// In handler: count with labels
m.IncrementCounter(context.Background(), "skill_create_total",
"tenant", "t_acme",
"visibility", "public",
)
m.IncrementCounter(context.Background(), "skill_delete_total",
"tenant", "t_acme",
"reason", "archived",
)
fmt.Println("ok")
}
Output: ok
Example (BusinessErrorMetrics) ¶
Example_businessErrorMetrics shows counting errors by error_code for alerting and dashboards.
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewCounter("errors_total", "Total errors by code")
// When business code returns an errorx error:
// err := errorx.NotFound("AIHUB_SKILL_NOT_FOUND", "...")
// m.IncrementCounter(ctx, "errors_total",
// "error_code", errorx.CodeOf(err).String(),
// "http_status", fmt.Sprintf("%d", errorx.HTTPStatusOf(err)),
// )
m.IncrementCounter(context.Background(), "errors_total",
"error_code", "AIHUB_SKILL_NOT_FOUND",
"http_status", "404",
)
m.IncrementCounter(context.Background(), "errors_total",
"error_code", "AIHUB_SKILL_QUERY_FAILED",
"http_status", "500",
)
fmt.Println("ok")
}
Output: ok
Example (BusinessGauge) ¶
Example_businessGauge shows tracking current state (queue depth, active sessions, etc.).
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewGauge("queue_depth", "Current queue depth")
// Worker reports current queue size periodically
queueSize := 42
m.SetGauge("queue_depth", float64(queueSize),
"queue", "skill_publish",
)
fmt.Println("ok")
}
Output: ok
Example (BusinessHistogram) ¶
Example_businessHistogram shows recording request latency.
package main
import (
"context"
"fmt"
"time"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewHistogram("request_seconds", "Request latency", metricsx.DefaultBuckets...)
start := time.Now()
// ... business logic ...
latency := time.Since(start).Seconds()
m.RecordHistogram(context.Background(), "request_seconds", latency,
"operation", "find_skill",
"status", "200",
"tenant", "t_acme",
)
fmt.Println("ok")
}
Output: ok
Example (BusinessMultiTenant) ¶
Example_businessMultiTenant shows per-tenant metric segmentation via labels.
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewCounter("api_requests_total", "Total API requests")
m.NewHistogram("api_request_seconds", "API request latency", metricsx.DefaultBuckets...)
// Per-tenant recording
tenants := []string{"t_acme", "t_globex", "t_initech"}
for _, tenant := range tenants {
m.IncrementCounter(context.Background(), "api_requests_total",
"tenant", tenant,
"endpoint", "/v1/skills",
)
m.RecordHistogram(context.Background(), "api_request_seconds", 0.05,
"tenant", tenant,
"endpoint", "/v1/skills",
)
}
fmt.Println("ok")
}
Output: ok
Example (BusinessNilManagerInTests) ¶
Example_businessNilManagerInTests shows that nil Manager is safe — tests can pass nil without nil-checks everywhere.
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// Test setup: pass nil Manager
var m metricsx.Manager // nil
m = metricsx.NewManager(nil, nil)
// Business code calls methods without nil-checks:
m.IncrementCounter(context.Background(), "test_counter")
m.RecordHistogram(context.Background(), "test_hist", 0.5)
m.SetGauge("test_gauge", 1.0)
fmt.Println("ok")
}
Output: ok
Example (BusinessSystemMetrics) ¶
Example_businessSystemMetrics shows that GetHandler auto-records Go runtime metrics (goroutines, memory) on each /metrics scrape.
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
metricsx.RegisterSystemMetrics(m)
// When Prometheus scrapes /metrics, these are auto-updated:
// app_go_goroutines
// app_sys_memory_alloc
// app_sys_total_alloc
// app_go_num_gc
// app_go_sys
handler := metricsx.GetHandler(m)
fmt.Println(handler != nil)
}
Output: true
Example (BusinessUpDownCounter) ¶
Example_businessUpDownCounter shows tracking values that go up and down (active sessions, in-flight requests).
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
m.NewUpDownCounter("active_sessions", "Active sessions")
// Session start: +1
m.DeltaUpDownCounter(context.Background(), "active_sessions", 1,
"tenant", "t_acme",
)
// Session end: -1
m.DeltaUpDownCounter(context.Background(), "active_sessions", -1,
"tenant", "t_acme",
)
fmt.Println("ok")
}
Output: ok
Index ¶
Examples ¶
- Package (BusinessBootstrap)
- Package (BusinessCardinalityWarning)
- Package (BusinessCounter)
- Package (BusinessErrorMetrics)
- Package (BusinessGauge)
- Package (BusinessHistogram)
- Package (BusinessMultiTenant)
- Package (BusinessNilManagerInTests)
- Package (BusinessSystemMetrics)
- Package (BusinessUpDownCounter)
- ErrAlreadyRegistered
- ErrNotRegistered
- GetHandler
- Manager (NilSafety)
- NewManager
- NewPrometheusManager
- Noop
- RegisterSystemMetrics
Constants ¶
This section is empty.
Variables ¶
var DefaultBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}
DefaultBuckets provides sensible histogram bucket boundaries for request latency in seconds. Use with NewHistogram:
m.NewHistogram("request_seconds", "Request latency",
metricsx.DefaultBuckets...)
Functions ¶
func GetHandler ¶
GetHandler returns an HTTP handler that serves:
- /metrics — Prometheus exposition format
- /debug/pprof/* — Go pprof profiling endpoints
It also records system metrics (goroutine count, memory usage, GC count) on each /metrics scrape.
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
handler := metricsx.GetHandler(m)
if handler != nil {
fmt.Println("ok")
}
}
Output: ok
func NewPrometheusMeter ¶
NewPrometheusMeter creates an otel Meter backed by a Prometheus exporter. Use this with NewManager to get a Manager that exposes /metrics via GetHandler.
meter := metricsx.NewPrometheusMeter("aihub", "v1.0.0")
m := metricsx.NewManager(meter, logger)
http.Handle("/metrics", metricsx.GetHandler(m))
func PromCounter ¶
func PromCounter(name, help string) prometheus.Counter
PromCounter returns a Prometheus counter (for direct use with promhttp instrumentation, bypassing the otel layer). Advanced use only — prefer Manager.NewCounter for business code.
func RegisterSystemMetrics ¶
func RegisterSystemMetrics(m Manager)
RegisterSystemMetrics registers the standard system metric names so they appear in the metric store before the first /metrics scrape. Call this at startup if you want the system metrics to always be present.
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
m := metricsx.Noop()
metricsx.RegisterSystemMetrics(m)
// Now app_go_goroutines, app_sys_memory_alloc, etc. are registered.
fmt.Println("ok")
}
Output: ok
Types ¶
type ErrAlreadyRegistered ¶
type ErrAlreadyRegistered struct{ Name string }
ErrAlreadyRegistered is returned when a metric name is registered twice.
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
err := &metricsx.ErrAlreadyRegistered{Name: "duplicate_metric"}
fmt.Println(err)
}
Output: metricsx: metric "duplicate_metric" already registered
func (*ErrAlreadyRegistered) Error ¶
func (e *ErrAlreadyRegistered) Error() string
type ErrNotRegistered ¶
type ErrNotRegistered struct{ Name string }
ErrNotRegistered is returned when recording against an unregistered name.
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
err := &metricsx.ErrNotRegistered{Name: "missing_metric"}
fmt.Println(err)
}
Output: metricsx: metric "missing_metric" not registered
func (*ErrNotRegistered) Error ¶
func (e *ErrNotRegistered) Error() string
type Manager ¶
type Manager interface {
// Registration (call at startup, before recording)
NewCounter(name, desc string)
NewUpDownCounter(name, desc string)
NewHistogram(name, desc string, buckets ...float64)
NewGauge(name, desc string)
// Recording (call in business code)
IncrementCounter(ctx context.Context, name string, labels ...string)
DeltaUpDownCounter(ctx context.Context, name string, value float64, labels ...string)
RecordHistogram(ctx context.Context, name string, value float64, labels ...string)
SetGauge(name string, value float64, labels ...string)
}
Manager defines the interface for registering and recording metrics. All methods are nil-safe — a nil Manager is a valid no-op implementation.
Example (NilSafety) ¶
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// A nil Manager is a valid no-op — methods don't panic.
// This happens when NewManager(nil, nil) is called.
m := metricsx.NewManager(nil, nil)
m.NewCounter("test", "test") // no-op
m.IncrementCounter(context.Background(), "test") // no-op
m.DeltaUpDownCounter(context.Background(), "test", 1.0) // no-op
m.RecordHistogram(context.Background(), "test", 0.5) // no-op
m.SetGauge("test", 1.0) // no-op
fmt.Println("ok")
}
Output: ok
func NewManager ¶
NewManager creates a Manager backed by the given otel meter. If logger is nil, a no-op logger is used.
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// In real code: meter := metricsx.NewPrometheusMeter("aihub", "v1.0.0")
// Here we pass nil meter to get a no-op Manager (safe for examples).
m := metricsx.NewManager(nil, nil)
fmt.Println(m == nil)
}
Output: false
func NewPrometheusManager ¶
NewPrometheusManager is a convenience that creates a Prometheus-backed Meter and wraps it in a Manager in one call.
m := metricsx.NewPrometheusManager("aihub", "v1.0.0", logger)
http.Handle("/metrics", metricsx.GetHandler(m))
Example ¶
package main
import (
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// NewPrometheusManager creates a Prometheus-backed Manager in one call.
// (Not run in example to avoid Prometheus registry conflicts.)
_ = metricsx.NewPrometheusManager
fmt.Println("see signature")
}
Output: see signature
func Noop ¶
func Noop() Manager
Noop returns a Manager that discards all operations. Useful in tests and disabled features.
Example ¶
package main
import (
"context"
"fmt"
"github.com/aisphereio/kernel/metricsx"
)
func main() {
// Noop returns a Manager that discards all operations.
// Useful in tests and disabled features.
m := metricsx.Noop()
m.NewCounter("test", "test counter")
m.IncrementCounter(context.Background(), "test")
fmt.Println("ok")
}
Output: ok