Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/metrics"
Defines the Client interface that service code instruments against, so the choice of metrics backend stays in main. Ships one no-op implementation; the real backends are opentel, prometheus and statsd.
The interface
Client covers the instrumentation points a service actually has, rather than exposing a generic counter/gauge/histogram vocabulary:
SqlOpen(driverName, dsn string) (*sql.DB, error)
InstrumentDB(name string, db *sql.DB) error
InstrumentHandler(path string, handler http.HandlerFunc) http.Handler
InstrumentRoundTripper(next http.RoundTripper) http.RoundTripper
MetricsHandlerFunc() http.HandlerFunc
IncLogLevelCounter(level string)
IncErrorCounter(task, operation, code string)
Close() error
Naming the integration points rather than the primitives is what makes the abstraction hold. A generic Counter(name string) interface leaks immediately, because Prometheus wants label dimensions declared up front, StatsD wants a dotted name, and OpenTelemetry wants attributes on an instrument. Wrapping an http.Handler is the same operation in all three.
SqlOpen sits in the interface because database instrumentation has to happen at connection time, not afterwards.
The no-op default is the useful part
Default implements every method as a no-op, with two exceptions that keep it usable rather than merely present:
SqlOpenreturns a real, working*sql.DB. A service using the default client still has a database.MetricsHandlerFuncreturns a handler answeringOK, so the metrics route exists and responds instead of 404ing.
This means a service can be fully wired for instrumentation from day one and run without any backend configured. Development, tests, and CLI tools all get the production code path with nothing to set up.
The alternative shapes are worse. A nil client means a nil check at every instrumentation site. Making the backend mandatory means tests need a metrics server.
The counters that are not derived
Two application-level counters are in the interface because nothing else can produce them:
IncLogLevelCounter(level) counts log records by severity. A rising rate of error records is a signal available before anyone reads the log, and it is the metric that most often catches a partial failure that has not yet affected latency or status codes.
IncErrorCounter(task, operation, code) records errors along three dimensions, which is enough to answer “which operation, in which task, failed how” without a log query.
logutil can drive the first one, so log-level counts stay consistent with what was actually logged.
Swapping backends
Application code depends on metrics.Client. Choosing a backend is one construction in main:
m, err := prometheus.New() // scrape-based
m, err := statsd.New() // push-based
m, err := opentel.New(ctx, name, version)
bootstrap takes the client and passes it to your bind function, so nothing below main knows which one is in use.
Close is in the interface because push-based backends buffer. Skipping it loses whatever had not yet flushed.
Dependencies
This package reaches no external module: it uses only the Go standard library. The backend packages carry their own.
Links
- Reference documentation
- Source code
- Backends: opentel, prometheus, statsd
- The observability guide: /docs/observability/
- All nurago packages