health

package
v1.0.0 Latest Latest
Warning

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

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

Documentation

Overview

Package health answers the two questions an orchestrator asks, which are not the same question.

liveness  — is this process broken beyond recovery? Restart me if not.
readiness — can this process serve traffic right now? Route to me if so.

Conflating them is the classic Kubernetes outage: a liveness probe that pings the database means a database blip restarts every replica simultaneously, turning a recoverable dependency failure into a total one. So Live here checks NOTHING but the process being able to answer, and Ready is where dependencies belong.

Example

Severity is the decision that matters. An optional dependency degrades the service; it does not take it out of rotation.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/IncepTools/go/src/web/health"
)

func main() {
	r := health.New("shop", "1.4.2")
	r.Register(
		health.Of("database", health.Critical, func(context.Context) error { return nil }),
		health.Of("cache", health.Optional, func(context.Context) error {
			return errors.New("connection refused")
		}),
	)

	report := r.Run(context.Background())
	fmt.Println("overall:", report.Status)
	fmt.Println("database:", report.Checks["database"].Status)
	fmt.Println("cache:", report.Checks["cache"].Status)
}
Output:
overall: DEGRADED
database: UP
cache: DEGRADED

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DetailHandler

func DetailHandler(r *Registry) fiber.Handler

DetailHandler returns the full report with per-check latency, always 200. It is for humans and dashboards, not for probes.

Put it behind authentication: the check names and messages describe your internal topology.

func LiveHandler

func LiveHandler() fiber.Handler

LiveHandler answers liveness. It checks nothing: if this code runs, the process is alive. Point the liveness probe here and nowhere else.

func ReadyHandler

func ReadyHandler(r *Registry) fiber.Handler

ReadyHandler answers readiness: 200 while the service can serve, 503 once a critical dependency is down. A DEGRADED report still returns 200 — that is what Optional means.

Types

type Check

type Check struct {
	Name      string         `json:"name"`
	Status    Status         `json:"status"`
	Message   string         `json:"message,omitempty"`
	LatencyMS int64          `json:"latencyMs"`
	Details   map[string]any `json:"details,omitempty"`
	Severity  string         `json:"severity,omitempty"`
}

Check is one dependency's result.

type Checker

type Checker interface {
	Name() string
	Severity() Severity
	Check(ctx context.Context) error
}

Checker probes one dependency.

An implementation MUST honour ctx: the registry gives each check a deadline, and a check that ignores it holds up the whole report — which is how a readiness endpoint ends up timing out and taking every replica out of rotation at once.

func Of

func Of(name string, sev Severity, fn func(ctx context.Context) error) Checker

Of builds a Checker from a function.

func PingChecker

func PingChecker(name string, sev Severity, ping func(ctx context.Context) error) Checker

PingChecker adapts anything with a context ping — a *sql.DB, a redis client, an HTTP probe — to a Checker.

type Registry

type Registry struct {

	// Timeout bounds each individual check. Defaults to 2s.
	Timeout time.Duration
	Service string
	Version string
	// contains filtered or unexported fields
}

Registry holds the checks a service runs. The zero value is usable.

func New

func New(service, version string) *Registry

New returns a registry stamped with the service identity.

func (*Registry) Register

func (r *Registry) Register(c ...Checker)

Register adds a checker.

func (*Registry) Run

func (r *Registry) Run(ctx context.Context) Report

Run probes every dependency CONCURRENTLY and aggregates.

Concurrently because sequentially is how a readiness endpoint with six two-second checks takes twelve seconds and gets killed by the probe timeout it was meant to satisfy.

Example (CriticalFailure)

A critical dependency failing is what takes the service out of rotation, and only that.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/IncepTools/go/src/web/health"
)

func main() {
	r := health.New("shop", "1.4.2")
	r.Register(health.Of("database", health.Critical,
		func(context.Context) error { return errors.New("connection refused") }))

	fmt.Println(r.Run(context.Background()).Status)
}
Output:
DOWN

type Report

type Report struct {
	Status  Status           `json:"status"`
	Service string           `json:"service,omitempty"`
	Version string           `json:"version,omitempty"`
	Uptime  string           `json:"uptime,omitempty"`
	Checks  map[string]Check `json:"checks,omitempty"`
}

Report is the aggregate.

type Severity

type Severity int

Severity says what a failing check means for the service as a whole.

const (
	// Critical means this failing takes the service out of rotation.
	Critical Severity = iota
	// Optional means this failing degrades the service but it keeps serving.
	//
	// Most caches, most third-party APIs and every analytics sink belong
	// here. A service that reports itself DOWN because an analytics endpoint
	// is slow has made an outage out of a nuisance.
	Optional
)

type Status

type Status string

Status is the outcome of a check.

const (
	// StatusUp means working.
	StatusUp Status = "UP"
	// StatusDegraded means working with reduced capability — a cache that is
	// down but is only a cache. A degraded service still takes traffic.
	StatusDegraded Status = "DEGRADED"
	// StatusDown means not working. A down service is removed from the load
	// balancer.
	StatusDown Status = "DOWN"
)

Jump to

Keyboard shortcuts

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