github.com/goforj/metrics provides a small in-memory metrics primitive for GoForj and other Go applications.
It is intentionally narrow:
- counters, gauges, and fixed-bucket histograms
- lock-free hot-path updates
- snapshot-based export
- Prometheus-compatible text exposition
- no global singleton requirement
go get github.com/goforj/metricspackage main
import (
"log"
"net/http"
"time"
"github.com/goforj/metrics"
)
func main() {
reg := metrics.NewRegistry()
requests := reg.MustCounter(metrics.Descriptor{
Name: "http.requests",
Help: "Total HTTP requests served.",
Kind: metrics.KindCounter,
})
latency := reg.MustHistogram(metrics.Descriptor{
Name: "http.request.duration",
Help: "HTTP request latency.",
Kind: metrics.KindHistogram,
Unit: metrics.UnitSeconds,
}, metrics.DurationBounds(metrics.DefaultDurationBounds()))
http.Handle("/metrics", metrics.Handler(reg))
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer latency.ObserveSince(start)
requests.Inc()
_, _ = w.Write([]byte("hello"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}- Register metrics once during startup.
- Update metrics on hot paths with atomics only.
- Export a point-in-time snapshot when Prometheus scrapes
/metrics.
Use dotted names internally for readability and hierarchy.
Examples:
http.requestshttp.request.durationjobs.processedscheduler.run.duration
Prometheus output normalizes those to underscore-separated metric names and appends conventional suffixes such as _total.
Supported units:
metrics.UnitNonemetrics.UnitSecondsmetrics.UnitBytesmetrics.UnitItems
Duration histograms should use UnitSeconds and DurationHistogram or Histogram.ObserveSince.
Expose the registry directly:
http.Handle("/metrics", metrics.Handler(reg))The exporter writes Prometheus text exposition from an immutable snapshot so exporters never touch live metric state directly.