health

package module
v0.1.0 Latest Latest
Warning

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

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

README

health

Thread-safe registry for runtime health and readiness checks of dependencies (databases, caches, message brokers, external HTTP services, etc.) — exposed via Kubernetes-style probes.

Zero third-party dependencies in the core. HTTP framework adapters (stdlib net/http, Gin, Chi, Echo, Fiber), observability adapters (OpenTelemetry, Prometheus), and concrete checkers (Postgres, Redis, NATS, generic HTTP probe, DNS) ship as separate modules under contrib/.

Install

go get github.com/ubgo/health

Quick start

package main

import (
    "context"
    "log"
    "time"

    "github.com/ubgo/health"
)

type pingChecker struct{ name string }

func (p *pingChecker) Name() string { return p.name }

func (p *pingChecker) Check(ctx context.Context) health.Result {
    // Run your dependency check here. Set Status, Error, Latency, etc.
    return health.Result{Status: health.StatusUp}
}

func main() {
    reg := health.NewRegistry()
    _ = reg.Register(&pingChecker{name: "db"})
    _ = reg.Register(&pingChecker{name: "cache"})

    ctx := context.Background()
    reg.Start(ctx, 30*time.Second) // background re-checks every 30s
    defer reg.Stop()

    snap := reg.SnapshotForProbe(health.ProbeReadiness)
    log.Printf("ready=%s", snap.Status)
}

Probe semantics

Probe When to fail it
ProbeLiveness Process is responding. Always Up. Used by k8s to decide whether to restart the pod.
ProbeReadiness Process is ready to serve traffic. Down if any critical check is Down; Degraded if any degraded-severity check is Down; otherwise Up. Informational checks never affect aggregate.
ProbeStartup Process has finished initialising. Mirrors readiness, but reports Unknown until the first time readiness is Up. Useful for k8s startup probes that should not flap once warm.

Severity

A failed check's Severity decides whether it fails the readiness probe:

Severity A Down check ⇒ readiness is Use for
SeverityCritical (default) Down DB, cache, primary message broker — anything you cannot serve without
SeverityDegraded Degraded (still 200 OK) Email provider, third-party APIs you can fall back from
SeverityInfo unchanged Purely informational — disk usage, version banner

Set per checker at registration:

reg.Register(emailChecker, health.WithSeverity(health.SeverityDegraded))

Per-check timeout

reg.Register(slowDB, health.WithTimeout(2*time.Second))

A check exceeding its timeout returns StatusDown with the deadline-exceeded error. Default: 5 seconds.

Observer pattern

Observers fire after every check — used by adapters (OTEL spans, Prometheus metrics, alerting webhooks) to react without polling:

reg.Subscribe(func(name string, r health.Result) {
    if r.Status == health.StatusDown {
        slack.Notify("check failed: " + name)
    }
})

Adapters

Adapter modules ship as separate Go modules under contrib/. Import only the ones you use; each pulls in its own dependencies.

HTTP framework renderers
Adapter Module path
health-nethttp github.com/ubgo/health/contrib/health-nethttp
health-gin github.com/ubgo/health/contrib/health-gin
health-chi github.com/ubgo/health/contrib/health-chi
health-echo github.com/ubgo/health/contrib/health-echo
health-fiber github.com/ubgo/health/contrib/health-fiber

Each exposes Liveness, Readiness, Startup handlers and a Mount helper with WithMiddleware / WithLivenessPath / WithReadinessPath / WithStartupPath options.

Observability
Adapter Module path
health-otel github.com/ubgo/health/contrib/health-otel
health-prom github.com/ubgo/health/contrib/health-prom
Concrete checkers
Adapter Module path
health-postgres github.com/ubgo/health/contrib/health-postgres
health-redis github.com/ubgo/health/contrib/health-redis
health-nats github.com/ubgo/health/contrib/health-nats
health-httpprobe github.com/ubgo/health/contrib/health-httpprobe
health-dns github.com/ubgo/health/contrib/health-dns

Adapters land incrementally as the core stabilises.

Compatibility

Requires Go 1.24 or later.

License

Apache License 2.0. See LICENSE and NOTICE.

Documentation

Overview

Package health is a thread-safe registry for runtime health and readiness checks of dependencies (databases, caches, message brokers, external HTTP services, etc.) exposed via Kubernetes-style probes.

The package has zero third-party dependencies and contains no HTTP types. All rendering — net/http, Gin, Chi, Echo, Fiber, OpenTelemetry, Prometheus, gRPC — lives in adapter modules under contrib/.

Typical use:

reg := health.NewRegistry()
reg.Register(myDBChecker)
reg.Register(myCacheChecker)
reg.Start(ctx, 30*time.Second) // background re-checks

snap := reg.SnapshotForProbe(health.ProbeReadiness)
if snap.Status != health.StatusUp { /* not ready */ }

Plug an HTTP framework adapter for endpoint exposure:

import healthnethttp "github.com/ubgo/health/contrib/health-nethttp"

mux := http.NewServeMux()
healthnethttp.Mount(mux, reg)

The Checker interface is the integration point for downstream plugins:

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

Index

Constants

This section is empty.

Variables

View Source
var ErrAlreadyRegistered = errors.New("health: checker already registered with that name")

ErrAlreadyRegistered is returned by Register when a checker with the given Name is already in the registry.

View Source
var ErrNotFound = errors.New("health: checker not registered with that name")

ErrNotFound is returned by Run when the requested checker name is not in the registry.

Functions

This section is empty.

Types

type Checker

type Checker interface {
	// Name returns a stable, unique identifier for this check (e.g. "db", "cache").
	// The name appears as a key in the registry snapshot and in HTTP responses.
	Name() string

	// Check runs the health check honouring ctx for cancellation/timeout.
	// Implementations should never panic; on any error, return a Result with
	// Status=StatusDown and Error populated.
	Check(ctx context.Context) Result
}

Checker performs a health check for a named component. Implementations must be safe for concurrent invocation by the Registry.

type Observer

type Observer func(name string, result Result)

Observer receives every Result the registry produces. Used by adapters (e.g. OTEL spans, Prometheus metrics, alert webhooks) to react without polling. Observers are invoked synchronously after each check; long-running observers should fan out to a goroutine themselves.

type Probe

type Probe string

Probe is one of the three Kubernetes-style probe types a registry can snapshot for. See SnapshotForProbe.

const (
	// ProbeLiveness — the process is alive. Fails only if the host has
	// crashed in a way that the process itself can detect; otherwise always up.
	ProbeLiveness Probe = "liveness"
	// ProbeReadiness — the process is ready to serve traffic. Aggregates the
	// worst critical Result from the registry.
	ProbeReadiness Probe = "readiness"
	// ProbeStartup — the process has finished initialising. Mirrors readiness
	// until the first up snapshot, then mirrors readiness from then on.
	ProbeStartup Probe = "startup"
)

type RegisterOption

type RegisterOption func(*registerConfig)

RegisterOption configures a Register call.

func WithSeverity

func WithSeverity(s Severity) RegisterOption

WithSeverity overrides the default severity (Critical).

func WithTimeout

func WithTimeout(d time.Duration) RegisterOption

WithTimeout caps how long the Check can run. Zero means no per-check timeout (use the parent ctx). Default: 5 seconds.

type Registry

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

Registry is the thread-safe central store of checkers and their last results.

func NewRegistry

func NewRegistry() *Registry

NewRegistry constructs an empty Registry.

func (*Registry) Register

func (r *Registry) Register(c Checker, opts ...RegisterOption) error

Register adds a checker. Returns ErrAlreadyRegistered if the name is taken.

func (*Registry) Run

func (r *Registry) Run(ctx context.Context, name string) (Result, error)

Run invokes a single checker by name and stores its Result. Returns ErrNotFound if the name is not registered.

func (*Registry) RunAll

func (r *Registry) RunAll(ctx context.Context)

RunAll invokes every registered checker concurrently and stores their Results in the registry. Returns once all checks complete or ctx is done.

func (*Registry) Snapshot

func (r *Registry) Snapshot() map[string]Result

Snapshot returns a copy of the current per-checker results. The returned map is owned by the caller and safe to mutate.

func (*Registry) SnapshotForProbe

func (r *Registry) SnapshotForProbe(probe Probe) Snapshot

SnapshotForProbe returns the registry's state aggregated for the given probe type:

  • ProbeLiveness: always StatusUp (the process is responding).
  • ProbeReadiness: StatusDown if any critical check is Down; StatusDegraded if any degraded-severity check is Down (and no critical is Down); otherwise StatusUp. SeverityInfo checks never affect the aggregate.
  • ProbeStartup: same as readiness, but reports StatusUnknown until the first time readiness has been observed Up — useful for k8s startup probes that should not flap once warm.

func (*Registry) Start

func (r *Registry) Start(ctx context.Context, interval time.Duration)

Start launches a background goroutine that calls RunAll on the given interval. If interval is zero or negative, RunAll runs once and returns. Start is safe to call multiple times — a second call replaces the loop from the first.

func (*Registry) Stop

func (r *Registry) Stop()

Stop terminates the background loop started by Start. Idempotent.

func (*Registry) Subscribe

func (r *Registry) Subscribe(o Observer)

Subscribe registers an Observer that will be invoked synchronously after every Check. Subscribe is safe to call concurrently with checks.

func (*Registry) Unregister

func (r *Registry) Unregister(name string)

Unregister removes a checker. Idempotent; no-op if name not registered.

type Result

type Result struct {
	Status    Status         `json:"status"`
	Severity  Severity       `json:"severity"`
	Message   string         `json:"message,omitempty"`
	Error     string         `json:"error,omitempty"`
	Latency   time.Duration  `json:"latency_ns,omitempty"`
	Timestamp time.Time      `json:"timestamp"`
	Metadata  map[string]any `json:"metadata,omitempty"`
}

Result of a single Check invocation.

type Severity

type Severity string

Severity controls whether a failed check fails the readiness probe.

const (
	// SeverityCritical: a Down result fails the readiness probe. Default for
	// most production dependencies (DB, cache, etc.).
	SeverityCritical Severity = "critical"
	// SeverityDegraded: a Down result is reported in the snapshot but the
	// readiness probe still returns up. Use for dependencies whose absence
	// degrades but does not break the service (e.g. an email provider).
	SeverityDegraded Severity = "degraded"
	// SeverityInfo: a Down result is reported but never affects either
	// liveness or readiness. Use for purely informational checks.
	SeverityInfo Severity = "info"
)

type Snapshot

type Snapshot struct {
	Probe  Probe             `json:"probe"`
	Status Status            `json:"status"`
	Time   time.Time         `json:"time"`
	Checks map[string]Result `json:"checks"`
}

Snapshot is the aggregate state of a probe at a point in time.

func (Snapshot) HTTPStatus

func (s Snapshot) HTTPStatus() int

HTTPStatus maps the snapshot to a conventional HTTP status code: 200 for Up or Degraded, 503 for Down or Unknown.

Returns a plain int — the core does not import net/http types into its API, but exposes the constant for convenience to adapters.

func (Snapshot) JSON

func (s Snapshot) JSON() ([]byte, error)

JSON returns the snapshot marshalled as JSON bytes.

type Status

type Status string

Status is the outcome of a single health check.

const (
	// StatusUp means the dependency responded successfully.
	StatusUp Status = "up"
	// StatusDown means the dependency failed or was unreachable.
	StatusDown Status = "down"
	// StatusDegraded means the dependency responded but in a reduced state
	// (slow, partial failure, fallback path active).
	StatusDegraded Status = "degraded"
	// StatusUnknown means the check has never run or its outcome is unclear.
	StatusUnknown Status = "unknown"
)

Directories

Path Synopsis
contrib
health-chi module
health-echo module
health-fiber module
health-gin module
health-otel module
health-prom module
health-redis module

Jump to

Keyboard shortcuts

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