metrics

package
v0.3.0-20260807201250-... Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

Metrics Utilities (platform/metrics)

The metrics package provides reusable helpers for emitting counters and histograms on a tally.Scope.

Design

Free functions on tally.Scope — no wrapper types. Existing constructors accept tally.Scope and do not need to change.

Operation lifecycleBegin and Complete tie operation metrics together. Begin captures the start time and emits {name}.start; Complete records duration and count on {name}.finish.

Result tagging — the finish histogram is tagged with result=success, result=error, or result=cancel. Cancellation is detected with errors.Is(err, context.Canceled). Callers that accumulate tags while the operation runs can pass them to Complete.

Consistent naming — named helpers follow the {name}.{sub} sub-scope pattern, producing metric paths such as process.start and publish.attempts.

Operation Lifecycle

For any operation with a clear start and end, use Begin and Complete:

Function Emits
Begin(scope, name, buckets, ...tags) {name}.start counter +1 and returns an Op
op.Complete(err, ...tags) {name}.finish histogram tagged with result=success|error|cancel and any completion tags

buckets is required at Begin because operations differ widely in expected latency. The finish histogram records both the duration distribution and the number of completed operations, so Complete does not emit a separate counter.

func (c *Controller) Process(ctx context.Context, delivery consumer.Delivery) (retErr error) {
    op := metrics.Begin(c.scope, "process", metrics.LongLatencyBuckets)
    defer func() { op.Complete(retErr) }()

    // ... business logic ...
    return nil
}

Tags passed to Begin apply to both lifecycle metrics. Tags known only after execution, such as an error classification, can be attached to the finish histogram:

err := controller.Process(ctx, delivery)
err = classifier.Process(err)
op.Complete(err, metrics.NewTag("origin", "infra_retryable"))

Named Helpers

For ad-hoc metrics that do not fit the operation lifecycle:

Function Emits Example
NamedCounter(scope, name, counter, value, ...tags) {name}.{counter} counter publish.attempts
NamedHistogram(scope, name, histogram, buckets, ...tags) {name}.{histogram} histogram process.duration
metrics.NamedCounter(c.scope, "publish", "attempts", 1)

h := metrics.NamedHistogram(c.scope, "process", "duration", metrics.FastLatencyBuckets)
h.RecordDuration(elapsed)

Do not emit gauges or timers. Represent operation latency and completion count with lifecycle histograms, and represent instantaneous quantities as sampled histogram values when needed.

Why histograms, not timers

Durations are recorded as histograms rather than timers. Timer percentiles cannot be combined accurately across time series, while bucketed histogram counts can be summed to reconstruct a combined distribution for correct aggregate percentiles.

Tags

Use NewTag to pass dimensional tags to a helper:

op := metrics.Begin(c.scope, "process", metrics.LongLatencyBuckets, metrics.NewTag("queue", req.Queue))
defer func() { op.Complete(retErr) }()

metrics.NamedCounter(c.scope, "publish", "attempts", 1, metrics.NewTag("topic", c.topic))

Latency Buckets

There is no default bucket set. The package exports three common sets:

Set Range Use for
FastLatencyBuckets ~100µs – 5s Fast in-process work such as scoring, cache lookups, and CPU-bound operations
StorageLatencyBuckets ~1ms – 1m Storage and message-queue round trips such as database reads, writes, publishing, and consuming
LongLatencyBuckets ~5ms – 4h Long-running pipeline work and external calls such as builds, merges, pushes, and provider calls

Pass one of these sets or a custom tally.DurationBuckets to Begin or NamedHistogram.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// FastLatencyBuckets suits fast in-process operations (~microseconds to
	// seconds): scoring, cache lookups, and other CPU-bound work.
	FastLatencyBuckets = tally.DurationBuckets{
		100 * time.Microsecond,
		250 * time.Microsecond,
		500 * time.Microsecond,
		1 * time.Millisecond,
		2500 * time.Microsecond,
		5 * time.Millisecond,
		10 * time.Millisecond,
		25 * time.Millisecond,
		50 * time.Millisecond,
		100 * time.Millisecond,
		250 * time.Millisecond,
		500 * time.Millisecond,
		1 * time.Second,
		2500 * time.Millisecond,
		5 * time.Second,
	}

	// StorageLatencyBuckets suits storage and message-queue round-trips
	// (~1ms to a minute): database reads/writes, publish/consume, and RPC
	// handlers whose latency is dominated by such calls.
	StorageLatencyBuckets = tally.DurationBuckets{
		1 * time.Millisecond,
		2500 * time.Microsecond,
		5 * time.Millisecond,
		10 * time.Millisecond,
		25 * time.Millisecond,
		50 * time.Millisecond,
		100 * time.Millisecond,
		250 * time.Millisecond,
		500 * time.Millisecond,
		1 * time.Second,
		2500 * time.Millisecond,
		5 * time.Second,
		10 * time.Second,
		30 * time.Second,
		1 * time.Minute,
	}

	// LongLatencyBuckets suits long-running pipeline work and external calls
	// (~5ms to hours): builds, merges, git pushes, and external provider calls.
	LongLatencyBuckets = tally.DurationBuckets{
		5 * time.Millisecond,
		10 * time.Millisecond,
		25 * time.Millisecond,
		50 * time.Millisecond,
		100 * time.Millisecond,
		250 * time.Millisecond,
		500 * time.Millisecond,
		1 * time.Second,
		2500 * time.Millisecond,
		5 * time.Second,
		10 * time.Second,
		30 * time.Second,
		1 * time.Minute,
		2 * time.Minute,
		5 * time.Minute,
		10 * time.Minute,
		30 * time.Minute,
		1 * time.Hour,
		2 * time.Hour,
		4 * time.Hour,
	}
)

Common duration bucket sets for latency histograms. Operations differ widely in expected latency, so there is no single default — pick the set whose range matches the operation and pass it to Begin or NamedHistogram. Buckets far outside an operation's real latency waste series cardinality and lose resolution where the data actually lands.

Functions

func NamedCounter

func NamedCounter(scope tally.Scope, name string, counter string, value int64, tags ...Tag)

NamedCounter increments the {name}.{counter} counter by value.

func NamedHistogram

func NamedHistogram(scope tally.Scope, name string, histogram string, buckets tally.Buckets, tags ...Tag) tally.Histogram

NamedHistogram returns a tally.Histogram at {name}.{histogram} with the given bucket configuration. Store the returned histogram and call RecordDuration or RecordValue on each invocation.

Types

type Op

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

Op tracks the lifecycle of a named operation. It captures the start time on creation, emits a {name}.start counter, and records the duration and result on a {name}.finish histogram when Complete is called.

Usage:

func (c *Controller) Process(ctx context.Context, delivery consumer.Delivery) (retErr error) {
    op := metrics.Begin(c.scope, "process", metrics.StorageLatencyBuckets)
    defer func() { op.Complete(retErr) }()
    // ... business logic ...
}

func Begin

func Begin(scope tally.Scope, name string, buckets tally.Buckets, tags ...Tag) Op

Begin starts a new operation. It emits a {name}.start counter, captures the start time, and retains the buckets used by Complete.

func (Op) Complete

func (o Op) Complete(err error, tags ...Tag)

Complete records elapsed time on the {name}.finish histogram, tagged with result=success|error|cancel and any additional tags accumulated while the operation ran. The histogram records both duration and count. Cancellation is detected through the error chain.

type Tag

type Tag struct {
	// Key is the tag name (e.g., "controller", "topic").
	Key string
	// Value is the tag value (e.g., "land", "request").
	Value string
}

Tag is a key-value pair attached to a metric for dimensional filtering.

func NewTag

func NewTag(key, value string) Tag

NewTag creates a Tag with the given key and value.

Jump to

Keyboard shortcuts

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