Help us shape the future of Go! Take the 2026 Go Developer Survey

check

package
v2.10.0 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package check standardizes /health and /ready endpoints. This allows you to easily know when your server is ready and healthy.

Index

Examples

Constants

View Source
const (
	// StatusFail indicates a specific check has failed.
	StatusFail Status = "fail"
	// StatusPass indicates a specific check has passed.
	StatusPass Status = "pass"

	// DefaultCheckName is the name of the default checker.
	DefaultCheckName = "internal"

	// NameHealth is the Name carried by the aggregate Response returned
	// from CheckHealth.
	NameHealth = "Health"
	// NameReady is the Name carried by the aggregate Response returned
	// from CheckReady.
	NameReady = "Ready"
)
View Source
const (
	MeasureGroupProbe   = "probe"
	MeasureKeyAge       = "age"
	MeasureKeyThreshold = "threshold"
)

A stale FreshnessResponse reports, under MeasureGroupProbe in UnitSeconds, how long ago the last probe completed (MeasureKeyAge) and the staleness budget it exceeded (MeasureKeyThreshold).

View Source
const DefaultProbeTimeout = 500 * time.Millisecond

DefaultProbeTimeout is the recommended upper bound for a single Check probe. It keeps aggregate /health latency bounded when a subsystem is wedged, while still leaving room for one slow round-trip.

View Source
const MsgNotReady = "not ready"

MsgNotReady is the message a ReadyGate reports when it has been neither fired by Ready nor failed by Fail: the subsystem is still initializing and there is nothing more to say about it.

View Source
const UnitSeconds = "seconds"

UnitSeconds is the Unit of a Measure whose values are durations. Durations are reported as float seconds, unrounded, whatever rounding the check's message applies.

Variables

View Source
var ErrDuplicateCheckName = errors.New("duplicate check name")

ErrDuplicateCheckName is returned when a check is registered under a name already registered in the same set (health or ready).

View Source
var ErrEmptyCheckName = errors.New("empty check name")

ErrEmptyCheckName is returned when a check is registered with an empty name.

Functions

func BoundDeadline added in v2.10.0

func BoundDeadline(ctx context.Context, max time.Duration) (context.Context, context.CancelFunc)

BoundDeadline caps ctx's deadline at max. If ctx already has a deadline at or inside max, it is returned unchanged with a no-op cancel; otherwise a child context with a max-duration deadline is returned. Callers must always defer the returned cancel.

Intended for Check implementations that want a bounded probe latency even when /health callers pass context.Background().

func HasCheck added in v2.10.0

func HasCheck(r Response, name string) bool

HasCheck reports whether r contains a sub-check with the given name.

Types

type BasicResponse added in v2.10.0

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

BasicResponse is the Response implementation. Construct via Pass/Info/Error/Fail/NamedPass/NamedFail/NewBasicResponse; the zero value has empty Name/Status/Message and is not safe to return from a Checker — its wire status would be the empty string, which the HTTP handler treats as pass.

measures is never modified after construction: WithMeasure builds a new map, so copies of a BasicResponse may share it, including across the goroutines that serve a frozen response.

func Error

func Error(err error) BasicResponse

Error is a utility function for creating a failing response from an error.

func Fail added in v2.10.0

func Fail(msg string) BasicResponse

Fail is a utility function for creating a failing response with a fixed message.

func Info

func Info(msg string, args ...interface{}) BasicResponse

Info is a utility function to generate a healthy status with a printf message.

func NamedFail added in v2.10.0

func NamedFail(name, msg string) BasicResponse

NamedFail builds a failing response with the given name and message.

func NamedPass added in v2.10.0

func NamedPass(name string) BasicResponse

NamedPass builds a passing response with the given name.

func NewBasicResponse added in v2.10.0

func NewBasicResponse(name string, status Status, message string, checks Responses) BasicResponse

NewBasicResponse builds a BasicResponse with every field set except measures, which WithMeasure adds. Used by CheckHealth/CheckReady to build the aggregate response. Rebuilding a response through here is also how the HTTP handler withholds detail: what it does not copy over, measures included, is not served.

func Pass

func Pass() BasicResponse

Pass is a utility function to generate a passing status response with the default parameters.

func (BasicResponse) Checks added in v2.10.0

func (b BasicResponse) Checks() Responses

func (BasicResponse) MarshalJSON added in v2.10.0

func (b BasicResponse) MarshalJSON() ([]byte, error)

MarshalJSON renders the fixed fields exactly as wireResponse always has, then one member per measure group, in sorted group order. With no groups the bytes are those of wireResponse alone, so a check that reports no numbers renders as it did before measures existed.

func (BasicResponse) Measures added in v2.10.0

func (b BasicResponse) Measures() Measures

func (BasicResponse) Message added in v2.10.0

func (b BasicResponse) Message() string

func (BasicResponse) Name added in v2.10.0

func (b BasicResponse) Name() string

func (BasicResponse) Status added in v2.10.0

func (b BasicResponse) Status() Status

func (*BasicResponse) UnmarshalJSON added in v2.10.0

func (b *BasicResponse) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the fixed fields into wireResponse, then takes as a measure group every other member that decodes as a Measure. Members that are not measure-shaped are ignored, as encoding/json ignores unknown fields: QueryHealthCheck decodes a whole /health body into a BasicResponse, and its version and commit strings must not fail that.

func (BasicResponse) WithMeasure added in v2.10.0

func (b BasicResponse) WithMeasure(group string, m Measure) BasicResponse

WithMeasure returns a copy of b reporting m under group, replacing any group of that name. m is copied, so the caller may reuse its map.

Invalid input is dropped rather than reported, because a check must always render: group, m.Unit and every value key must be non-empty runs of [a-z0-9_]; group may not be one of the check's own fields (name, status, message, checks); no value key may be "unit"; and non-finite values are discarded. If no value survives, b is returned unchanged.

func (BasicResponse) WithName added in v2.10.0

func (b BasicResponse) WithName(name string) BasicResponse

WithName returns a copy of b with the given name. Used by namedChecker and startupChecker to override the inner checker's name without an extra wrapper.

type Check

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

Check holds the named health and ready checks served by /health and /ready. Names are unique within each set; the same name may appear once in the health set and once in the ready set.

func NewCheck

func NewCheck() *Check

NewCheck returns an empty Check with no default checkers registered.

Example
h := NewCheck()
h.CheckHealth(context.Background())

func (*Check) AddNamedHealthCheck added in v2.10.0

func (c *Check) AddNamedHealthCheck(nc NamedChecker) error

AddNamedHealthCheck registers nc as a health check under nc.CheckName(); nc.Check is responsible for stamping Response.Name (see NamedChecker), so no additional wrapping happens here.

It returns an error wrapping ErrEmptyCheckName for an empty name, or ErrDuplicateCheckName for a name already registered as a health check; the earlier registration is kept. A ready check of the same name does not conflict.

A registration after Freeze is dropped and returns nil; see there.

func (*Check) AddNamedReadyCheck added in v2.10.0

func (c *Check) AddNamedReadyCheck(nc NamedChecker) error

AddNamedReadyCheck registers nc as a ready check. See AddNamedHealthCheck for naming, uniqueness, and what a registration after Freeze does; ready names are unique among ready checks only.

func (*Check) CheckHealth

func (c *Check) CheckHealth(ctx context.Context) Response

CheckHealth evaluates c's set of health checks and returns a populated Response.

Example
h := NewCheck()
err := h.AddNamedHealthCheck(Named("google", CheckerFunc(func(ctx context.Context) Response {
	var r net.Resolver
	_, err := r.LookupHost(ctx, "google.com")
	if err != nil {
		return Error(err)
	}
	return Pass()
})))
if err != nil {
	fmt.Println(err)
	return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
h.CheckHealth(ctx)

func (*Check) CheckReady

func (c *Check) CheckReady(ctx context.Context) Response

CheckReady evaluates c's set of ready checks and returns a populated Response.

func (*Check) Freeze added in v2.10.0

func (c *Check) Freeze(ctx context.Context)

Freeze replaces every registered health and ready check with a static snapshot of what it reports now, so CheckHealth and CheckReady go on returning that same answer for the life of the process.

It exists for terminal states. A process on its way out tears its subsystems down, and their checks then report that deliberate teardown as a fresh failure; because Responses sort failures first and then by name, a closed store can outrank -- and so mask -- the failure that made the process terminal. Freezing first preserves the report as it stood when that decision was made.

Each snapshot is flattened into a BasicResponse so a live Response cannot keep moving inside the frozen set: a *FreshnessResponse ages into a staleness failure on its own once its prober stops. Both render the same JSON object, so a frozen body has the same shape as the one served a moment earlier.

The registered names and their order are unchanged -- each frozen check keeps the CheckName it was registered under, not the name its response carried -- so ReadyCheckNames reports what it did before and no name can become duplicated. Only the values are pinned.

Freeze is terminal and first-freeze-wins: a second call is a no-op, there is no thaw, and checks registered afterwards are ignored. A registration racing the freeze may or may not be captured, which is why the caller must be the one thing still running.

Every probe is bounded on its own, at DefaultProbeTimeout, rather than out of one budget shared by the whole set; see probe. ctx is their parent, so a deadline on it still caps the freeze as a whole -- give it one only as a backstop, generous enough that a healthy freeze never reaches it. Once it expires the remaining probes run on a dead context, and a cancelled probe records the freeze itself rather than the state being frozen. For the same reason, pass a context that a signal cannot cancel.

func (*Check) ReadyCheckNames added in v2.10.0

func (c *Check) ReadyCheckNames() []string

ReadyCheckNames returns the names of currently-registered ready checks in registration order. Registration rejects empty and duplicate names, so every entry is non-empty and distinct.

type Checker

type Checker interface {
	Check(ctx context.Context) Response
}

Checker indicates a service whose health can be checked.

Check must return promptly. ctx carries a deadline, but that deadline is only cooperative: nothing can interrupt a Check that has already blocked. A wedged implementation hangs the /health or /ready request that called it, and it hangs Freeze -- neither the per-probe DefaultProbeTimeout that Freeze applies nor any backstop its caller wraps around the freeze as a whole can preempt the call. A check that never returns can therefore hold a process open past the teardown the freeze was taken to precede.

So keep uninterruptible work off the check path. Two patterns cover it:

  • Probe in the background and have Check report the last result. This is the only option when the probe cannot be cancelled at all: a bbolt View runs to completion whatever its caller wants. bolt.KVStore does this -- a prober goroutine refreshes a FreshnessResponse on a ticker and Check returns that value, so the uncancellable read never runs inside a probe.

  • Probe inline only through an API that honors ctx, and bound it with BoundDeadline so the latency stays capped even when the caller passes a context with no deadline of its own. sqlite.SqlStore.Check does this around PingContext.

Check may be called concurrently, and it may be called after the subsystem it reports on has been closed -- teardown does not deregister checks. Report that state as a failure rather than panicking on it.

func ErrCheck

func ErrCheck(fn func() error) Checker

ErrCheck will create a health checker that executes a function. If the function returns an error, it will return an unhealthy response. Otherwise, it will be as if the Ok function was called. Note: it is better to use CheckerFunc, because with Check, the context is ignored.

type CheckerFunc

type CheckerFunc func(ctx context.Context) Response

CheckerFunc is an adapter of a plain func() error to the Checker interface.

func (CheckerFunc) Check

func (f CheckerFunc) Check(ctx context.Context) Response

Check implements Checker.

type FreshnessResponse added in v2.10.0

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

FreshnessResponse is a Response implementation that ages out: callers (typically a background prober goroutine) push an underlying Response via Update; if no Update arrives within the configured staleness budget, Status() flips to StatusFail and Message() reports how long it has been since the last probe.

FreshnessResponse never copies safely; pass it by pointer.

func NewFreshnessResponse added in v2.10.0

func NewFreshnessResponse(name string, staleness time.Duration) *FreshnessResponse

NewFreshnessResponse returns an empty FreshnessResponse with the given name and staleness budget. Until Update is first called, Status() returns StatusFail and Message() reports msgNoProbe.

func (*FreshnessResponse) Checks added in v2.10.0

func (f *FreshnessResponse) Checks() Responses

Checks returns the underlying probe's nested checks when fresh; otherwise nil.

func (*FreshnessResponse) MarshalJSON added in v2.10.0

func (f *FreshnessResponse) MarshalJSON() ([]byte, error)

MarshalJSON emits the wire shape from a single snapshot, so the rendered JSON object reflects exactly one state, rendered by BasicResponse.MarshalJSON.

func (*FreshnessResponse) Measures added in v2.10.0

func (f *FreshnessResponse) Measures() Measures

Measures returns nothing before the first probe, the probe group when the snapshot is stale, and the underlying probe's Measures otherwise.

func (*FreshnessResponse) Message added in v2.10.0

func (f *FreshnessResponse) Message() string

Message returns a "no probe" / "stale" message when the snapshot is missing or aged out; otherwise it returns the underlying probe's Message.

func (*FreshnessResponse) Name added in v2.10.0

func (f *FreshnessResponse) Name() string

Name returns the configured name.

func (*FreshnessResponse) Snapshot added in v2.10.0

func (f *FreshnessResponse) Snapshot() BasicResponse

Snapshot renders f from one atomic load, implementing HealthSnapshotter. Reading every field through the four interface methods would be correct (each does its own atomic load) but could observe two different snapshots across the call sequence, yielding a combination that was never true: a stale status carried alongside the previous probe's empty message, which /health renders as the bare word "fail".

The returned BasicResponse fixes f's own fields. Its Checks are the underlying probe's, which this type does not own and does not copy.

func (*FreshnessResponse) Status added in v2.10.0

func (f *FreshnessResponse) Status() Status

Status returns StatusFail when no probe has run yet or the last probe is older than the staleness budget; otherwise it returns the underlying probe's Status.

func (*FreshnessResponse) Update added in v2.10.0

func (f *FreshnessResponse) Update(r Response)

Update records r as the latest probe result with the current monotonic timestamp. Safe to call concurrently with Name/Status/ Message/Checks/MarshalJSON.

type HealthSnapshotter added in v2.10.0

type HealthSnapshotter interface {
	Snapshot() BasicResponse
}

HealthSnapshotter is implemented by a Response that can render its entire state from a single coherent read. A Response whose fields derive from mutable state -- FreshnessResponse -- can otherwise report a torn combination, a status taken from one observation and a message from the next, because the four accessors each read independently.

Implementations must return a BasicResponse whose own fields are fixed; nested Checks may still be live, and snapshot recurses into them.

type Measure added in v2.10.0

type Measure struct {
	Unit   string
	Values map[string]float64
}

Measure is a group of numbers sharing one unit, reported by a check beside its message so a poller can read them without parsing the message text. It renders as a flat JSON object of its values plus a "unit" string:

{"completed":94,"total":200,"unit":"shards"}

A Measure attached to a response through WithMeasure is copied and validated; see there for the rules.

func (Measure) MarshalJSON added in v2.10.0

func (m Measure) MarshalJSON() ([]byte, error)

MarshalJSON renders the values in sorted key order followed by "unit", so the bytes are deterministic. A non-finite value fails the marshal, as it would for any float64; WithMeasure drops them before they get here.

func (*Measure) UnmarshalJSON added in v2.10.0

func (m *Measure) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts an object whose "unit" is a string and whose every other member is a number. Anything else is an error, which is how BasicResponse.UnmarshalJSON tells a measure group from an unrelated key.

type Measures added in v2.10.0

type Measures map[string]Measure

Measures maps a group name to its Measure. Each group renders as its own key on the check object, beside name, status and message.

type NamedChecker

type NamedChecker interface {
	Checker
	CheckName() string
}

NamedChecker is a superset of Checker that also indicates the name of the service. Implementations MUST stamp Response.Name() with CheckName() before returning from Check, so that AddNamedHealthCheck / AddNamedReadyCheck can register the value without an extra wrapper. Use Named to attach a name to a Checker that does not already do so.

func Named

func Named(name string, checker Checker) NamedChecker

Named returns a NamedChecker that delegates to checker and stamps name onto the response. Callers attach a human-readable name to a response without encoding that logic into the underlying check.

func NamedFunc

func NamedFunc(name string, fn CheckerFunc) NamedChecker

NamedFunc is the same as Named except it takes a CheckerFunc.

type ReadyGate added in v2.10.0

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

ReadyGate is a NamedChecker that reports StatusFail until Ready is called, after which it reports StatusPass. It is intended for gating readiness on subsystem init phases that complete once at startup.

A gate whose phase failed outright can be latched with Fail, after which it reports the failure message in place of MsgNotReady.

func NewReadyGate added in v2.10.0

func NewReadyGate(name string) *ReadyGate

NewReadyGate returns a ReadyGate that initially reports StatusFail.

func (*ReadyGate) Check added in v2.10.0

func (g *ReadyGate) Check(context.Context) Response

Check returns StatusPass once Ready has been called, otherwise StatusFail. A gate latched by Fail reports StatusFail carrying the failure message, whatever Ready and Unready have done. The response is stamped with the gate's name to satisfy the NamedChecker contract.

func (*ReadyGate) CheckName added in v2.10.0

func (g *ReadyGate) CheckName() string

CheckName returns the configured name of the gate.

func (*ReadyGate) Fail added in v2.10.0

func (g *ReadyGate) Fail(err error)

Fail latches a terminal startup failure carrying err's message, so the gate reports why it will never become ready rather than a bare MsgNotReady. The first error wins; a nil err is ignored.

Startup-only: Fail outranks both Ready and Unready and cannot be cleared, so a runtime degradation monitor must call Unready, never Fail.

func (*ReadyGate) Ready added in v2.10.0

func (g *ReadyGate) Ready()

Ready flips the gate to report StatusPass.

func (*ReadyGate) Unready added in v2.10.0

func (g *ReadyGate) Unready()

Unready flips the gate to report StatusFail.

type Response

type Response interface {
	Name() string
	Status() Status
	Message() string
	Checks() Responses
	// Measures returns the numbers the check reports beside its message,
	// grouped by unit. The result is shared and must not be modified.
	Measures() Measures
}

Response is the result of a single health check.

Implementations derive Status() and Message() at the moment they are called, so a stateful implementation (e.g. FreshnessResponse) can flip a previously-passing response to fail when its cached snapshot becomes stale. Every implementation marshals to the same wire shape: {"name","status","message"?,"checks"?,"<group>"?...}, one key per Measures group. BasicResponse renders it in MarshalJSON; FreshnessResponse and renamedResponse render through a BasicResponse snapshot.

A stateful implementation should also implement HealthSnapshotter, so callers that need every field from one observation -- rendering, or freezing a terminal report -- can take it without reading each accessor separately.

func Rename added in v2.10.0

func Rename(r Response, name string) Response

Rename returns r with its reported name replaced by name. A BasicResponse is rewritten by value via WithName; any other Response is wrapped in a renamedResponse so the inner type's state (e.g. FreshnessResponse) is not copied. Used by namedChecker and by callers that compose a Response themselves and need to stamp a name without knowing the concrete implementation.

type Responses

type Responses []Response

Responses is a sortable collection of Response objects.

func (Responses) Len

func (r Responses) Len() int

func (Responses) Less

func (r Responses) Less(i, j int) bool

Less defines the order in which responses are sorted.

Failing responses are always sorted before passing responses. Responses with the same status are then sorted according to the name of the check.

func (Responses) Swap

func (r Responses) Swap(i, j int)

func (*Responses) UnmarshalJSON added in v2.10.0

func (r *Responses) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON array of check objects, allocating a BasicResponse for each element and lifting them into the Response interface slice. encoding/json cannot do this directly because []Response is a slice of interface values with no concrete type.

type Status

type Status string

Status string to indicate the overall status of the check.

Directories

Path Synopsis
Package checktest holds the helpers shared by tests that compare two renderings of a /health or /ready document.
Package checktest holds the helpers shared by tests that compare two renderings of a /health or /ready document.

Jump to

Keyboard shortcuts

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