diff --git a/README.md b/README.md index 2b20a88..2eb5727 100755 --- a/README.md +++ b/README.md @@ -7,58 +7,112 @@ [![GitHub release](https://img.shields.io/github/release/hamba/statter.svg)](https://github.com/hamba/statter/releases) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/hamba/statter/master/LICENSE) -Go stats clients +`statter` is a Go library for recording counters, gauges, histograms, and +timings through interchangeable reporters. -## Overview - -Install with: +## Install ```shell go get github.com/hamba/statter/v2 ``` -#### Supported stats clients -* **L2met** Writes l2met to a `Logger` interface -* **Statsd** Writes statsd to `UDP` -* **Prometheus** Exposes stats via `HTTP` -* **VictoriaMetrics** Exposes stats via `HTTP` - -**Note:** This project has renamed the default branch from `master` to `main`. You will need to update your local environment. +## Quick start -## Usage +Create a reporter, pass it to `statter.New` with a positive flush interval, and +close the root statter when the application is shutting down. Closing flushes +pending values and closes the reporter if it implements `io.Closer`. ```go -reporter := statsd.New(statsdAddr, "") -stats := statter.New(reporter, 10*time.Second).With("my-prefix") +package main + +import ( + "time" + + "github.com/hamba/statter/v2" + "github.com/hamba/statter/v2/reporter/statsd" + "github.com/hamba/statter/v2/tags" +) + +func main() { + reporter, err := statsd.New("127.0.0.1:8125", "") + if err != nil { + panic(err) + } + + root := statter.New(reporter, 10*time.Second) + defer root.Close() + + stats := root.With("my-service") + stats.Counter("requests", tags.Str("route", "/")).Inc(1) + stats.Gauge("queue-depth").Set(3) + stats.Histogram("response-size").Observe(512) + stats.Timing("request-duration").Observe(25 * time.Millisecond) +} +``` + +Counters report the accumulated increment since the previous flush and reset +after each flush. Gauges retain their last value and are reported on every +flush. Histograms and timings are sent directly when the reporter supports +them; otherwise, statter aggregates observations between flushes and reports +count, sum, mean, standard deviation, minimum, maximum, and configured +percentiles. + +## Names and tags + +Use `With` to apply a shared name prefix and tags to metrics. Prefixes are joined +using `.` by default, and tags with the same key are overridden by the newer +value. The separator can be changed with `WithSeparator`. -stats.Counter("my-counter", tags.Str("tag", "value")).Inc(1) +```go +serviceStats := stats.With("worker", tags.Str("region", "west")) +serviceStats.Counter("jobs", tags.Str("queue", "email")).Inc(1) ``` -### Scopes +`WithContext` attaches tags to a context, and `FromContext` adds those tags to a +statter. This is useful for carrying request-specific tags through functions +without passing a scoped statter explicitly. -A `Scope` is a sub-statter identified by an id, which tracks the metrics created -through it. +## Scopes -Requesting a scope with an existing id but different tags removes the previous -scope and every metric created through it. This keeps a metric unique for a set -of tags, such as a gauge carrying a revision that changes over time: +A `Scope` tracks the metrics created through it so that they can be removed as +a group. Scope IDs are unique across the statter tree. Requesting an existing +ID with different tags or a different parent deletes its previous metrics and +replaces the scope. This is useful for metrics that represent a replaceable +set of labels, such as build or revision information: ```go -stats.Scope("build-info", tags.Str("revision", rev)).Gauge("info").Set(1) +stats.Scope("build-info", tags.Str("revision", revision)). +Gauge("info"). +Set(1) ``` -The tracked metrics can also be removed together as a batch: +Delete a scope explicitly when its metrics are no longer relevant: ```go -scope := stats.Scope("tenant:"+id, tags.Str("tenant", id)) +scope := stats.Scope("tenant:"+tenantID, tags.Str("tenant", tenantID)) scope.Counter("requests").Inc(1) -scope.Timing("latency").Observe(d) - -scope.Delete() // Both metrics are removed. +scope.Timing("latency").Observe(25 * time.Millisecond) +scope.Delete() ``` -Use `HasScope` to determine if a scope currently exists. +Metrics are removed from the backend only when the reporter supports removal. +Otherwise deletion stops local aggregation, but the reporter may retain the +last values it received. + +Only metrics first registered through the Scope are tracked by it. If the same +metric was registered before it was requested through a Scope, deleting the +Scope does not remove that pre-existing metric. + +## Reporters + +The base `Reporter` interface handles counters and gauges. A reporter may also +implement histogram and timing interfaces to receive those observations +directly, and removable interfaces to remove backend metrics when they are +deleted. -**Note:** Metrics are only removed from the backend if the reporter supports -removal, which the Prometheus and VictoriaMetrics reporters do. Otherwise -deletion only stops local aggregation. +| Reporter | Backend | +|-------------------------------------------------------------------------------------------------------|--------------------------------------------------------------| +| [`reporter/l2met`](https://pkg.go.dev/github.com/hamba/statter/v2/reporter/l2met) | Writes metrics to a `hamba/logger` logger | +| [`reporter/statsd`](https://pkg.go.dev/github.com/hamba/statter/v2/reporter/statsd) | Sends metrics to StatsD over UDP | +| [`reporter/prometheus`](https://pkg.go.dev/github.com/hamba/statter/v2/reporter/prometheus) | Exposes metrics through an HTTP handler | +| [`reporter/victoriametrics`](https://pkg.go.dev/github.com/hamba/statter/v2/reporter/victoriametrics) | Exposes metrics in Prometheus format through an HTTP handler | diff --git a/context.go b/context.go index d3fc579..11195ad 100644 --- a/context.go +++ b/context.go @@ -8,9 +8,10 @@ type ctxTags struct { tags []Tag } -// WithContext returns a new context with the given tags attached. If tags are -// already present in ctx, the new tags are merged with them; a new tag whose -// key already exists overrides the stored value. +// WithContext returns a context derived from ctx with tags attached. If ctx +// already carries tags added by WithContext, the new tags are merged and a +// value with the same key replaces the existing value. With no tags, it +// returns ctx unchanged. func WithContext(ctx context.Context, tags ...Tag) context.Context { if len(tags) == 0 { return ctx @@ -31,8 +32,8 @@ func WithContext(ctx context.Context, tags ...Tag) context.Context { return context.WithValue(ctx, contextKey{}, &ctxTags{tags: merged}) } -// FromContext returns a sub-statter whose tags include those stored in ctx by -// WithContext. If no tags are found in ctx, s is returned unchanged. +// FromContext returns a sub-statter with the tags stored in ctx by +// WithContext. If ctx has no such tags, it returns s unchanged. func (s *Statter) FromContext(ctx context.Context) *Statter { ct, _ := ctx.Value(contextKey{}).(*ctxTags) if ct == nil || len(ct.tags) == 0 { diff --git a/doc.go b/doc.go index 8759221..8fef116 100644 --- a/doc.go +++ b/doc.go @@ -1,10 +1,11 @@ // Package statter collects and reports statistics via a pluggable [Reporter]. // -// Stats are aggregated in memory and flushed to the reporter on a fixed -// interval. Counters accumulate deltas between flushes; gauges hold their -// last-set value. Histograms and timings are either delegated directly to -// the reporter (when it implements [HistogramReporter] / [TimingReporter]) -// or aggregated locally and emitted as a set of derived gauges and a counter. +// Stats are collected in memory and flushed to the reporter on a fixed +// interval. Counters report their accumulated delta and reset after each +// flush; gauges retain and report their last-set value. Histograms and timings +// are either delegated directly to the reporter (when it implements +// [HistogramReporter] or [TimingReporter]) or aggregated locally and emitted +// as derived gauges and a count counter. // // Metrics are identified by a name and an optional set of key/value [Tag] // pairs. The [Statter.With] method creates a scoped sub-statter that @@ -13,15 +14,15 @@ // instance. // // The [Statter.Scope] method returns a [Scope], a sub-statter identified by a -// caller supplied id. A Scope tracks the metrics created through it, so they -// can be removed together with [Scope.Delete]. Requesting a Scope with an -// existing id but different tags deletes the previous Scope and its metrics, -// which keeps metrics unique for a set of tags, such as a gauge carrying a -// revision that changes over time. +// caller-supplied ID. Newly registered metrics created through a Scope are +// tracked and can be removed together with [Scope.Delete]. Requesting a Scope +// with an existing ID but different tags or a different parent deletes the +// previous Scope and its metrics. This keeps metrics unique for a set of tags, +// such as a gauge carrying a revision that changes over time. // -// The [Reporter] interface is the only contract that backend adapters must -// satisfy. Richer adapters may additionally implement [HistogramReporter], -// [TimingReporter], and the corresponding Removable* interfaces. Metrics are -// only removed from the backend if the reporter implements the relevant -// Removable* interface, otherwise deletion only stops local aggregation. +// Backend adapters must satisfy [Reporter]. They may additionally implement +// [HistogramReporter] and [TimingReporter] to receive observations directly, +// or the corresponding Removable* interfaces to remove metrics from the +// backend. Without a removable interface, deletion stops local aggregation +// but cannot remove values already reported to the backend. package statter diff --git a/reporter/l2met/l2met.go b/reporter/l2met/l2met.go index d551e5e..4ababeb 100644 --- a/reporter/l2met/l2met.go +++ b/reporter/l2met/l2met.go @@ -1,4 +1,5 @@ -// Package l2met implements a l2met stats client. +// Package l2met reports counters and gauges as structured log fields using +// hamba/logger. package l2met import ( @@ -7,7 +8,7 @@ import ( "github.com/hamba/statter/v2/internal/bytes" ) -// L2met is a l2met client. +// L2met reports metrics to a logger. type L2met struct { log *logger.Logger prefix string @@ -15,7 +16,7 @@ type L2met struct { pool bytes.Pool } -// New returns a l2met reporter. +// New returns an L2met reporter that prepends prefix to metric names. func New(log *logger.Logger, prefix string) *L2met { if len(prefix) > 0 { prefix += "." diff --git a/reporter/prometheus/prometheus.go b/reporter/prometheus/prometheus.go index eef58c2..d353453 100644 --- a/reporter/prometheus/prometheus.go +++ b/reporter/prometheus/prometheus.go @@ -1,4 +1,4 @@ -// Package prometheus implements a prometheus stats client. +// Package prometheus exposes metrics through a Prometheus HTTP handler. package prometheus import ( @@ -15,17 +15,19 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -// Option represents a prometheus option function. +// Option configures a Prometheus reporter created by New. type Option func(p *Prometheus) -// WithBuckets sets the buckets to use with histograms. +// WithBuckets sets the default bucket boundaries used by histograms and +// timings unless a metric is registered with explicit buckets. func WithBuckets(buckets []float64) Option { return func(p *Prometheus) { p.defBuckets = buckets } } -// WithErrorLog sets the error log function for logging errors when registering metrics. +// WithErrorLog sets the function used to report errors while registering +// metrics. If fn is nil, the default error logger is retained. func WithErrorLog(fn func(string)) Option { return func(p *Prometheus) { if fn == nil { @@ -35,7 +37,7 @@ func WithErrorLog(fn func(string)) Option { } } -// Prometheus is a prometheus stats collector. +// Prometheus collects metrics for scraping in Prometheus format. type Prometheus struct { namespace string @@ -53,7 +55,7 @@ type Prometheus struct { errLog func(string) } -// New returns a new prometheus reporter. +// New returns a Prometheus reporter with the given namespace and options. func New(namespace string, opts ...Option) *Prometheus { fqn := newFQN() @@ -72,7 +74,8 @@ func New(namespace string, opts ...Option) *Prometheus { return p } -// Handler returns the prometheus HTTP handler for scraping metrics. +// Handler returns an HTTP handler that exposes the collected metrics for +// Prometheus scraping. func (p *Prometheus) Handler() http.Handler { return promhttp.HandlerFor(p.reg, promhttp.HandlerOpts{}) } diff --git a/reporter/statsd/statsd.go b/reporter/statsd/statsd.go index 9db1b87..5a3a799 100644 --- a/reporter/statsd/statsd.go +++ b/reporter/statsd/statsd.go @@ -1,4 +1,4 @@ -// Package statsd implements a statsd client. +// Package statsd reports metrics to a StatsD server over UDP. package statsd import ( @@ -21,19 +21,19 @@ func defaultConfig() config { } } -// Option represents a statsd option function. +// Option configures a Statsd reporter created by New. type Option func(*config) -// WithFlushInterval sets the maximum flushInterval for packet sending. -// Defaults to 300ms. +// WithFlushInterval sets the maximum interval between UDP flushes. The +// default is 300 milliseconds. func WithFlushInterval(interval time.Duration) Option { return func(c *config) { c.flushInterval = interval } } -// WithFlushBytes sets the maximum UDP packet size in bytes that will be sent. -// Defaults to 1432 bytes. +// WithFlushBytes sets the maximum UDP packet size in bytes. The default is +// 1432 bytes. func WithFlushBytes(bytes int) Option { return func(c *config) { c.flushBytes = bytes @@ -47,7 +47,9 @@ type Statsd struct { es statsd.ExtendedStatSender } -// New returns a statsd reporter. +// New returns a StatsD reporter configured with the server address, metric +// prefix, and options. It returns an error if the underlying client cannot be +// created. func New(addr, prefix string, opts ...Option) (*Statsd, error) { cfg := defaultConfig() for _, o := range opts { diff --git a/reporter/victoriametrics/victoria_metrics.go b/reporter/victoriametrics/victoria_metrics.go index 39b66d7..7eb69c3 100644 --- a/reporter/victoriametrics/victoria_metrics.go +++ b/reporter/victoriametrics/victoria_metrics.go @@ -1,4 +1,5 @@ -// Package victoriametrics implements a VictoriaMetrics stats reporter. +// Package victoriametrics exposes metrics in Prometheus format through an +// HTTP handler backed by VictoriaMetrics metrics. package victoriametrics import ( @@ -11,14 +12,14 @@ import ( "github.com/hamba/statter/v2/internal/bytes" ) -// VictoriaMetrics is a victoria metrics stats reporter. +// VictoriaMetrics collects metrics for HTTP scraping. type VictoriaMetrics struct { fqn *fqn set *metrics.Set } -// New returns a new victoria metrics reporter. +// New returns an empty VictoriaMetrics reporter. func New() *VictoriaMetrics { fqn := newFQN() diff --git a/scope.go b/scope.go index fd03db1..790edfc 100644 --- a/scope.go +++ b/scope.go @@ -11,10 +11,14 @@ type deletable interface { Delete() } -// Scope is a sub-statter identified by a unique id. +// Scope is a sub-statter identified by an ID unique across the statter tree. // -// Metrics created through a Scope are tracked by it, and are removed together -// when the Scope is deleted or replaced. See [Statter.Scope]. +// Newly registered metrics created through a Scope are tracked by it and +// removed together when the Scope is deleted or replaced. A metric requested +// through the Scope that was already registered elsewhere is not tracked. +// Metrics first registered through the Scope share their registration with +// later callers using the same name and tags, so deleting the Scope removes +// those metrics for all callers. See [Statter.Scope]. type Scope struct { reg *registry id string @@ -68,13 +72,14 @@ func (sc *Scope) matches(parent *Statter, tags []Tag) bool { return true } -// FullName returns the full name with prefix for the given name. +// FullName joins name to the Scope's prefix with the configured separator. +// If the Scope has no prefix, it returns name unchanged. func (sc *Scope) FullName(name string) string { return sc.s.FullName(name) } -// Counter returns a counter for the given name and tags, tracking it against -// the scope. +// Counter returns a counter for the given name and tags. A newly registered +// counter is tracked by the Scope and removed when the Scope is deleted. func (sc *Scope) Counter(name string, tags ...Tag) *Counter { c, created := sc.s.counter(name, tags) if created { @@ -83,8 +88,8 @@ func (sc *Scope) Counter(name string, tags ...Tag) *Counter { return c } -// Gauge returns a gauge for the given name and tags, tracking it against the -// scope. +// Gauge returns a gauge for the given name and tags. A newly registered gauge +// is tracked by the Scope and removed when the Scope is deleted. func (sc *Scope) Gauge(name string, tags ...Tag) *Gauge { g, created := sc.s.gauge(name, tags) if created { @@ -93,8 +98,9 @@ func (sc *Scope) Gauge(name string, tags ...Tag) *Gauge { return g } -// Histogram returns a histogram for the given name and tags, tracking it -// against the scope. +// Histogram returns a histogram for the given name and tags. A newly +// registered histogram is tracked by the Scope and removed when the Scope is +// deleted. func (sc *Scope) Histogram(name string, tags ...Tag) *Histogram { h, created := sc.s.histogram(name, tags) if created { @@ -103,8 +109,8 @@ func (sc *Scope) Histogram(name string, tags ...Tag) *Histogram { return h } -// Timing returns a timing for the given name and tags, tracking it against the -// scope. +// Timing returns a timing for the given name and tags. A newly registered +// timing is tracked by the Scope and removed when the Scope is deleted. func (sc *Scope) Timing(name string, tags ...Tag) *Timing { t, created := sc.s.timing(name, tags) if created { diff --git a/statter.go b/statter.go index 6f99d84..94d4645 100644 --- a/statter.go +++ b/statter.go @@ -10,44 +10,59 @@ import ( "github.com/hamba/statter/v2/internal/stats" ) -// DiscardReporter is a reporter that discards all stats. +// DiscardReporter discards all reported metrics. var DiscardReporter = discardReporter{} -// Reporter represents a stats reporter. -// -// The statter passes tags sorted by key. +// Reporter receives counter deltas and gauge values from a Statter. +// The statter passes tags sorted by key. Counter values are increments since +// the previous flush; gauge values are the latest value set. type Reporter interface { + // Counter adds a counter delta for the named metric and tags. Counter(name string, v int64, tags [][2]string) + + // Gauge sets the gauge value for the named metric and tags. Gauge(name string, v float64, tags [][2]string) } -// RemovableReporter represents a stats reporter that handles removal. +// RemovableReporter allows a reporter to remove counters and gauges previously +// reported to it. type RemovableReporter interface { + // RemoveCounter removes the counter identified by its name and tags. RemoveCounter(name string, tags [][2]string) + + // RemoveGauge removes the gauge identified by its name and tags. RemoveGauge(name string, tags [][2]string) } -// HistogramReporter represents a stats reporter that handles histograms. +// HistogramReporter lets a reporter receive histogram observations directly. type HistogramReporter interface { + // Histogram returns a function that reports observations for the named + // histogram and tags. Histogram(name string, tags [][2]string) func(v float64) } -// RemovableHistogramReporter represents a stats reporter that handles histogram removal. +// RemovableHistogramReporter allows a reporter to remove histograms previously +// created through HistogramReporter. type RemovableHistogramReporter interface { + // RemoveHistogram removes the histogram identified by its name and tags. RemoveHistogram(name string, tags [][2]string) } -// TimingReporter represents a stats reporter that handles timings. +// TimingReporter lets a reporter receive timing observations directly. type TimingReporter interface { + // Timing returns a function that reports durations for the named timing and + // tags. Timing(name string, tags [][2]string) func(v time.Duration) } -// RemovableTimingReporter represents a stats reporter that handles timing removal. +// RemovableTimingReporter allows a reporter to remove timings previously +// created through TimingReporter. type RemovableTimingReporter interface { + // RemoveTiming removes the timing identified by its name and tags. RemoveTiming(name string, tags [][2]string) } -// Tag is a stat tag. +// Tag is a key/value pair attached to a metric. type Tag = [2]string type config struct { @@ -66,56 +81,62 @@ func defaultConfig() config { } } -// Option represents a statter option function. +// Option configures a Statter created by New. type Option func(*config) -// WithPrefix sets the initial prefix on a statter. +// WithPrefix sets the prefix applied to metric names recorded by the Statter. func WithPrefix(prefix string) Option { return func(c *config) { c.prefix = prefix } } -// WithTags sets the initial tags on a statter. +// WithTags sets tags applied to metrics recorded by the Statter. A metric tag +// with the same key overrides the configured value. func WithTags(tags ...Tag) Option { return func(c *config) { c.tags = tags } } -// WithSeparator sets the key separator on a statter. +// WithSeparator sets the string used to join a prefix and metric name. func WithSeparator(sep string) Option { return func(c *config) { c.separator = sep } } -// WithPercentileSamples sets the number of samples taken to -// calculate percentiles. +// WithPercentileSamples sets the maximum number of observations retained to +// estimate percentiles for locally aggregated histograms and timings. The +// default is 1000; other aggregate statistics use all observations. func WithPercentileSamples(n int) Option { return func(c *config) { c.percSamples = n } } -// WithPercentiles sets the percentiles to calculate and report -// for aggregated histograms and timings. +// WithPercentiles sets the percentiles to report for locally aggregated +// histograms and timings. Each value is a percentile from 0 through 100. +// Percentiles are ignored when the reporter handles observations directly. func WithPercentiles(p []float64) Option { return func(c *config) { c.percentiles = p } } -// Statter collects and reports stats. +// Statter records metrics and periodically reports them to a Reporter. +// Create a Statter with New; its zero value is not ready to use. type Statter struct { reg *registry prefix string tags []Tag } -// New returns a Statter that aggregates stats and flushes them to r on every -// interval tick. Options may be used to set an initial prefix, tags, key -// separator, and percentile configuration. +// New returns a Statter that reports metrics to r on each interval tick. +// interval must be positive. Options configure the initial prefix and tags, +// name separator, and percentile aggregation. Call Close on the returned root +// Statter to stop reporting, flush pending metrics, and close r when it +// implements io.Closer. func New(r Reporter, interval time.Duration, opts ...Option) *Statter { cfg := defaultConfig() @@ -184,7 +205,8 @@ func (s *Statter) Reporter() Reporter { return s.reg.r } -// FullName returns the full name with prefix for the given name. +// FullName joins name to the Statter's prefix with the configured separator. +// If the Statter has no prefix, it returns name unchanged. func (s *Statter) FullName(name string) string { if s.prefix != "" { return s.prefix + s.reg.cfg.separator + name @@ -192,7 +214,7 @@ func (s *Statter) FullName(name string) string { return name } -// HasCounter determines if the counter exists. +// HasCounter reports whether a counter with name and tags has been registered. func (s *Statter) HasCounter(name string, tags ...Tag) bool { k := s.key(name, tags) @@ -237,7 +259,7 @@ func (s *Statter) counter(name string, tags []Tag) (*Counter, bool) { return c, created } -// HasGauge determines if the gauge exists. +// HasGauge reports whether a gauge with name and tags has been registered. func (s *Statter) HasGauge(name string, tags ...Tag) bool { k := s.key(name, tags) @@ -282,7 +304,8 @@ func (s *Statter) gauge(name string, tags []Tag) (*Gauge, bool) { return g, created } -// HasHistogram determines if the histogram exists. +// HasHistogram reports whether a histogram with name and tags has been +// registered. func (s *Statter) HasHistogram(name string, tags ...Tag) bool { k := s.key(name, tags) @@ -329,7 +352,7 @@ func (s *Statter) histogram(name string, tags []Tag) (*Histogram, bool) { return h, created } -// HasTiming determines if the timing exists. +// HasTiming reports whether a timing with name and tags has been registered. func (s *Statter) HasTiming(name string, tags ...Tag) bool { k := s.key(name, tags) @@ -396,9 +419,9 @@ func (s *Statter) mergeDescriptors(name string, tags []Tag) (string, []Tag) { return mergeDescriptors(s.prefix, s.reg.cfg.separator, name, s.tags, tags) } -// Close stops the reporting loop, flushes any pending stats to the reporter, -// and closes the reporter if it implements [io.Closer]. Close must be called -// on the root statter; calling it on a sub-statter returns an error. +// Close stops the reporting loop, flushes pending metrics, and closes the +// reporter if it implements [io.Closer]. Call Close on the root Statter; +// calling it on a sub-statter returns an error. func (s *Statter) Close() error { if err := s.reg.Close(s); err != nil { return err @@ -413,8 +436,8 @@ func (s *Statter) Close() error { return nil } -// Counter implements a counter that monotonically accumulates a value between -// flushes. The accumulated delta is reported and reset to zero on each flush. +// Counter accumulates increments between flushes. Each flush reports the +// accumulated delta and resets it to zero. type Counter struct { name string tags [][2]string @@ -424,12 +447,13 @@ type Counter struct { val atomic.Int64 } -// Inc increments the counter by v. +// Inc adds v to the counter. The increment may be positive, zero, or negative. func (c *Counter) Inc(v int64) { c.val.Add(v) } -// Delete removes the counter. +// Delete stops aggregating the counter and removes it from the reporter when +// the reporter implements RemovableReporter. func (c *Counter) Delete() { c.reg.reportMu.RLock() defer c.reg.reportMu.RUnlock() @@ -447,8 +471,7 @@ func (c *Counter) value() int64 { return c.val.Swap(0) } -// Gauge implements a gauge that holds its last-set value and reports it on -// each flush. +// Gauge holds its last-set value and reports it on each flush. type Gauge struct { name string tags [][2]string @@ -458,22 +481,22 @@ type Gauge struct { val atomic.Uint64 } -// Set sets the gauge value. +// Set replaces the gauge value. func (g *Gauge) Set(v float64) { g.val.Store(math.Float64bits(v)) } -// Inc increments the gauge by 1. +// Inc increases the gauge by 1. func (g *Gauge) Inc() { g.Add(1) } -// Dec decrements the gauge by 1. +// Dec decreases the gauge by 1. func (g *Gauge) Dec() { g.Add(-1) } -// Add increases the gauge's value by v. +// Add adds v to the gauge's current value. func (g *Gauge) Add(v float64) { for { oldBits := g.val.Load() @@ -484,12 +507,13 @@ func (g *Gauge) Add(v float64) { } } -// Sub subtracts the argument from the gauge's value. +// Sub subtracts v from the gauge's current value. func (g *Gauge) Sub(v float64) { g.Add(v * -1) } -// Delete removes the gauge. +// Delete stops reporting the gauge and removes it from the reporter when the +// reporter implements RemovableReporter. func (g *Gauge) Delete() { g.reg.reportMu.RLock() defer g.reg.reportMu.RUnlock() @@ -508,7 +532,7 @@ func (g *Gauge) value() float64 { return math.Float64frombits(v) } -// Histogram implements a histogram. +// Histogram records observations for a metric. // // When the reporter implements [HistogramReporter], observations are delegated // to it directly. Otherwise observations are aggregated locally and reported @@ -546,7 +570,7 @@ func newHistogram(hr HistogramReporter, name string, tags [][2]string, pool *sta } } -// Observe observes a histogram value. +// Observe adds v to the histogram. func (h *Histogram) Observe(v float64) { if h.hrFn != nil { h.hrFn(v) @@ -558,7 +582,8 @@ func (h *Histogram) Observe(v float64) { h.mu.Unlock() } -// Delete removes the histogram. +// Delete stops recording the histogram and removes it from the reporter when +// the reporter implements RemovableHistogramReporter or RemovableReporter. func (h *Histogram) Delete() { h.reg.reportMu.RLock() defer h.reg.reportMu.RUnlock() @@ -586,7 +611,7 @@ func (h *Histogram) value() *stats.Sample { return s } -// Timing implements a timing. +// Timing records duration observations for a metric. // // When the reporter implements [TimingReporter], observations are delegated to // it directly. Otherwise observations are aggregated locally in milliseconds @@ -625,10 +650,8 @@ func newTiming(tr TimingReporter, name string, tags [][2]string, pool *stats.Poo } } -// Observe observes a timing duration. -// -// If the reporter does not handle timings, the duration -// will be aggregated in milliseconds. +// Observe adds d to the timing. When the reporter does not handle timings, +// durations are aggregated and reported in milliseconds. func (t *Timing) Observe(d time.Duration) { if t.trFn != nil { t.trFn(d) @@ -640,7 +663,8 @@ func (t *Timing) Observe(d time.Duration) { t.mu.Unlock() } -// Delete removes the timing. +// Delete stops recording the timing and removes it from the reporter when the +// reporter implements RemovableTimingReporter or RemovableReporter. func (t *Timing) Delete() { t.reg.reportMu.RLock() defer t.reg.reportMu.RUnlock() diff --git a/tags/tags.go b/tags/tags.go index 1ca9a81..ac0ad8e 100644 --- a/tags/tags.go +++ b/tags/tags.go @@ -1,4 +1,5 @@ -// Package tags implements statter tags convenience functions. +// Package tags provides helpers for creating [statter.Tag] values from common +// Go types. package tags import ( @@ -7,23 +8,22 @@ import ( "github.com/hamba/statter/v2" ) -// Str returns a string tag with the given key and value. +// Str returns a tag whose value is v. func Str(k, v string) statter.Tag { return [2]string{k, v} } -// Int returns an int tag with the given key and value. +// Int returns a tag whose value is the base-10 representation of v. func Int(k string, v int) statter.Tag { return [2]string{k, strconv.Itoa(v)} } -// Int64 returns an int64 tag with the given key and value. +// Int64 returns a tag whose value is the base-10 representation of v. func Int64(k string, v int64) statter.Tag { return [2]string{k, strconv.FormatInt(v, 10)} } -// StatusCode returns a tag with the given key and the status -// code in the form '2xx'. +// StatusCode returns a tag whose value is the status class, such as "2xx". func StatusCode(k string, v int) statter.Tag { code := strconv.Itoa(v) return [2]string{k, string(code[0]) + "xx"}