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 — just like the HTTP and gRPC instrumentation.

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.