Skip to content

Instrument business logic (RED)

Not everything worth measuring is an HTTP request or an RPC. Operations gives any piece of business logic the same RED signals (Rate, Errors, Duration) by wrapping a function. It's the generic instrument for "how often does this run, how often does it fail, and how long does it take?"

ops, err := m.NewOperations()
if err != nil { return err }

err = ops.Observe(ctx, "reindex", func() error {
    return search.Reindex(ctx)
})

That one call records, labelled by the operation name:

Metric Type
operations_total{operation} counter
operation_errors_total{operation} counter (incremented when the fn returns an error)
operation_duration_seconds{operation} histogram
operations_in_flight{operation} gauge

The function's error is returned unchanged, so Observe drops cleanly into existing code:

if err := ops.Observe(ctx, "settle_payment", func() error {
    return payments.Settle(ctx, id)
}); err != nil {
    return err
}

Returning a value

Use ObserveResult when the operation produces a value:

report, err := metrics.ObserveResult(ops, ctx, "build_report", func() (Report, error) {
    return builder.Build(ctx)
})

Exemplars

If m has an exemplar source, the operation_duration_seconds sample carries the current trace_id, so a slow operation on a dashboard links to its trace, as the HTTP and gRPC instrumentation does.

Keep the operation name bounded

The operation label must come from a small, fixed set of names ("reindex", "settle_payment"): a compile-time constant, not an interpolated id. See Cardinality safety.

Options

NewOperations accepts the shared helper options: WithNamespace, WithBuckets, WithConstLabels. See Instrument resources for the same set.