Documentation
¶
Overview ¶
Package check standardizes /health and /ready endpoints. This allows you to easily know when your server is ready and healthy.
Index ¶
- Constants
- Variables
- func BoundDeadline(ctx context.Context, max time.Duration) (context.Context, context.CancelFunc)
- func HasCheck(r Response, name string) bool
- type BasicResponse
- func Error(err error) BasicResponse
- func Fail(msg string) BasicResponse
- func Info(msg string, args ...interface{}) BasicResponse
- func NamedFail(name, msg string) BasicResponse
- func NamedPass(name string) BasicResponse
- func NewBasicResponse(name string, status Status, message string, checks Responses) BasicResponse
- func Pass() BasicResponse
- func (b BasicResponse) Checks() Responses
- func (b BasicResponse) MarshalJSON() ([]byte, error)
- func (b BasicResponse) Measures() Measures
- func (b BasicResponse) Message() string
- func (b BasicResponse) Name() string
- func (b BasicResponse) Status() Status
- func (b *BasicResponse) UnmarshalJSON(data []byte) error
- func (b BasicResponse) WithMeasure(group string, m Measure) BasicResponse
- func (b BasicResponse) WithName(name string) BasicResponse
- type Check
- func (c *Check) AddNamedHealthCheck(nc NamedChecker) error
- func (c *Check) AddNamedReadyCheck(nc NamedChecker) error
- func (c *Check) CheckHealth(ctx context.Context) Response
- func (c *Check) CheckReady(ctx context.Context) Response
- func (c *Check) Freeze(ctx context.Context)
- func (c *Check) ReadyCheckNames() []string
- type Checker
- type CheckerFunc
- type FreshnessResponse
- func (f *FreshnessResponse) Checks() Responses
- func (f *FreshnessResponse) MarshalJSON() ([]byte, error)
- func (f *FreshnessResponse) Measures() Measures
- func (f *FreshnessResponse) Message() string
- func (f *FreshnessResponse) Name() string
- func (f *FreshnessResponse) Snapshot() BasicResponse
- func (f *FreshnessResponse) Status() Status
- func (f *FreshnessResponse) Update(r Response)
- type HealthSnapshotter
- type Measure
- type Measures
- type NamedChecker
- type ReadyGate
- type Response
- type Responses
- type Status
Examples ¶
Constants ¶
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" )
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).
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.
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.
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 ¶
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).
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
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().
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 ¶
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 ¶
CheckReady evaluates c's set of ready checks and returns a populated Response.
func (*Check) Freeze ¶ added in v2.10.0
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
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 ¶
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.
type CheckerFunc ¶
CheckerFunc is an adapter of a plain func() error to the Checker interface.
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
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
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
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
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 ¶
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
NewReadyGate returns a ReadyGate that initially reports StatusFail.
func (*ReadyGate) Check ¶ added in v2.10.0
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) Fail ¶ added in v2.10.0
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.
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
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) Less ¶
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) UnmarshalJSON ¶ added in v2.10.0
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.