health

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: Apache-2.0 Imports: 12 Imported by: 2

README

health-kit

Go Reference Go Report Card License codecov

中文文档

A unified health check toolkit for Go services. This package provides health check interfaces, probe implementations, multi-probe aggregation, and HTTP handlers compatible with both Fiber and net/http.

Features

  • Checker Interface: Unified health check interface for all probes
  • Built-in Probes: Redis, HTTP, Database, and Custom probes
  • Parallel Aggregation: Run multiple health checks in parallel with aggregated results
  • HTTP Handlers: Standard library and Fiber-compatible /health endpoint handlers
  • Kubernetes Support: Dedicated liveness and readiness probe handlers
  • IP Whitelisting: Restrict health endpoint access to specific IPs/CIDRs
  • Critical Checks: Distinguish between critical and non-critical dependencies
  • Latency Tracking: Measure and report health check latency

Installation

go get github.com/soulteary/health-kit

Usage

Basic Health Check
import (
    health "github.com/soulteary/health-kit"
)

// Create a configuration
config := health.DefaultConfig().
    WithServiceName("myservice").
    WithTimeout(5 * time.Second)

// Create an aggregator
aggregator := health.NewAggregator(config)

// Add checkers
aggregator.AddCheckers(
    health.NewRedisChecker(redisClient),
    health.NewHTTPChecker("herald", "http://herald:8080/healthz"),
)

// Perform health check
result := aggregator.Check(context.Background())
fmt.Printf("Status: %s\n", result.Status)
Redis Health Check
// Basic Redis checker
redisChecker := health.NewRedisChecker(redisClient)

// With custom name and timeout
redisChecker := health.NewRedisCheckerWithName("session-redis", redisClient).
    WithTimeout(2 * time.Second)
HTTP Dependency Check
// Check external service health
httpChecker := health.NewHTTPChecker("herald", "http://herald:8080/healthz").
    WithTimeout(3 * time.Second).
    WithExpectedCode(http.StatusOK)

// Check with custom HTTP method
httpChecker := health.NewHTTPChecker("api", "http://api/status").
    WithMethod(http.MethodHead)
Database Health Check
// Basic database checker
dbChecker := health.NewDBChecker(db)

// With custom name
dbChecker := health.NewDBCheckerWithName("postgres", db).
    WithTimeout(5 * time.Second)
Custom Health Check
// Create a custom checker
customChecker := health.NewCustomChecker("cache", func(ctx context.Context) error {
    if cache.Size() == 0 {
        return errors.New("cache is empty")
    }
    return nil
}).WithTimeout(1 * time.Second)
Disabled Checker
// For optional dependencies that are not configured
disabledChecker := health.NewDisabledChecker("optional-redis").
    WithMessage("Redis is not configured")
HTTP Handlers (Standard Library)
import (
    "net/http"
    health "github.com/soulteary/health-kit"
)

// Full health check with all probes
http.HandleFunc("/health", health.Handler(aggregator))

// Kubernetes liveness probe (always returns OK if service is running)
http.HandleFunc("/livez", health.LivenessHandler("myservice"))

// Kubernetes readiness probe (checks all dependencies)
http.HandleFunc("/readyz", health.ReadinessHandler(aggregator))

// Simple health check without probes
http.HandleFunc("/health", health.SimpleHandler("myservice"))
Fiber Handlers
import (
    "github.com/gofiber/fiber/v2"
    health "github.com/soulteary/health-kit"
)

app := fiber.New()

// Full health check
app.Get("/health", health.FiberHandler(aggregator))
app.Get("/healthz", health.FiberHandler(aggregator))

// Kubernetes liveness probe
app.Get("/livez", health.FiberLivenessHandler("myservice"))

// Kubernetes readiness probe
app.Get("/readyz", health.FiberReadinessHandler(aggregator))

// Simple health check
app.Get("/health", health.SimpleFiberHandler("myservice"))
Configuration Options
config := health.DefaultConfig().
    WithServiceName("herald").
    WithTimeout(5 * time.Second).
    WithIPWhitelist([]string{"10.0.0.0/8", "192.168.1.1"}).
    WithTrustedProxies([]string{"10.0.0.0/8"}). // Trust only your reverse proxies
    WithDetails(true).          // Include detailed response
    WithChecks(true).           // Include individual check results
    WithCriticalChecks([]string{"redis", "database"})  // Critical dependencies
Critical vs Non-Critical Checks
// Define which checks are critical
config := health.DefaultConfig().
    WithCriticalChecks([]string{"redis", "database"})

aggregator := health.NewAggregator(config)
aggregator.AddCheckers(
    health.NewRedisChecker(redisClient),          // Critical
    health.NewDBChecker(db),                       // Critical  
    health.NewHTTPChecker("cache", cacheURL),      // Non-critical
)

result := aggregator.Check(ctx)
// If Redis or DB fails: Status = "unhealthy"
// If only cache fails: Status = "degraded"
// If all pass: Status = "ok"
IP Whitelisting
config := health.DefaultConfig().
    WithIPWhitelist([]string{
        "127.0.0.1",        // Localhost
        "10.0.0.0/8",       // Private network CIDR
        "192.168.1.100",    // Specific IP
    })

// Requests from non-whitelisted IPs will receive 403 Forbidden
Trusted Proxies (Forwarded Headers)

If your service sits behind a reverse proxy or load balancer, configure trusted proxy IPs/CIDRs before relying on X-Forwarded-For or X-Real-IP headers. Untrusted sources will be ignored to prevent header spoofing.

config := health.DefaultConfig().
    WithIPWhitelist([]string{"192.168.1.100"}).
    WithTrustedProxies([]string{"10.0.0.0/8"}) // Only proxy IPs are trusted
Production Privacy

Health responses include detailed dependency metadata by default. Consider disabling details and individual checks in production to avoid leaking internal state.

Project Structure

health-kit/
├── checker.go         # Checker interface, result types, and JSON marshaling
├── config.go          # Configuration with IP whitelist support
├── probes.go          # Built-in probes (Redis, HTTP, DB, Custom, Disabled)
├── aggregator.go      # Multi-probe aggregation with parallel execution
├── handler.go         # HTTP handlers for Fiber and net/http
└── *_test.go          # Comprehensive tests

Integration Examples

Herald (OTP Service)
package main

import (
    "github.com/gofiber/fiber/v2"
    health "github.com/soulteary/health-kit"
    "github.com/redis/go-redis/v9"
)

func main() {
    redisClient := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
    
    config := health.DefaultConfig().
        WithServiceName("herald").
        WithTimeout(2 * time.Second)
    
    aggregator := health.NewAggregator(config)
    aggregator.AddChecker(health.NewRedisChecker(redisClient))
    
    app := fiber.New()
    app.Get("/healthz", health.FiberHandler(aggregator))
    
    app.Listen(":8080")
}
Stargate (Auth Gateway)
package main

import (
    "net/http"
    health "github.com/soulteary/health-kit"
)

func main() {
    config := health.DefaultConfig().
        WithServiceName("stargate").
        WithTimeout(5 * time.Second).
        WithCriticalChecks([]string{"redis"})
    
    aggregator := health.NewAggregator(config)
    aggregator.AddCheckers(
        health.NewRedisChecker(redisClient),
        health.NewHTTPChecker("herald", "http://herald:8080/healthz").WithTimeout(2*time.Second),
        health.NewHTTPChecker("warden", "http://warden:8080/health").WithTimeout(2*time.Second),
    )
    
    http.HandleFunc("/health", health.Handler(aggregator))
    http.HandleFunc("/livez", health.LivenessHandler("stargate"))
    http.HandleFunc("/readyz", health.ReadinessHandler(aggregator))
    
    http.ListenAndServe(":8080", nil)
}
Warden (User Service)
package main

import (
    "net/http"
    health "github.com/soulteary/health-kit"
)

func main() {
    config := health.DefaultConfig().
        WithServiceName("warden").
        WithIPWhitelist([]string{"10.0.0.0/8", "127.0.0.1"}).
        WithTimeout(5 * time.Second)
    
    aggregator := health.NewAggregator(config)
    
    // Redis is optional for Warden in ONLY_LOCAL mode
    if redisEnabled {
        aggregator.AddChecker(health.NewRedisChecker(redisClient))
    } else {
        aggregator.AddChecker(health.NewDisabledChecker("redis"))
    }
    
    // Check data loaded
    aggregator.AddChecker(health.NewCustomChecker("data_loaded", func(ctx context.Context) error {
        if userCache.Len() == 0 {
            return errors.New("no users loaded")
        }
        return nil
    }))
    
    http.HandleFunc("/health", health.Handler(aggregator))
    http.HandleFunc("/healthcheck", health.Handler(aggregator))
    
    http.ListenAndServe(":8080", nil)
}

Response Format

Detailed Response (default)
{
  "status": "ok",
  "service": "myservice",
  "checks": {
    "redis": {
      "name": "redis",
      "status": "ok",
      "latency_ms": 5,
      "timestamp": "2024-01-25T10:30:00Z"
    },
    "database": {
      "name": "database",
      "status": "ok",
      "latency_ms": 12,
      "timestamp": "2024-01-25T10:30:00Z",
      "metadata": {
        "open_connections": 10,
        "in_use": 5,
        "idle": 5
      }
    }
  },
  "timestamp": "2024-01-25T10:30:00Z",
  "total_latency_ms": 15
}
Simple Response (WithDetails(false))
{
  "status": "ok",
  "service": "myservice"
}
Degraded Response
{
  "status": "degraded",
  "service": "myservice",
  "checks": {
    "redis": {
      "name": "redis",
      "status": "ok",
      "latency_ms": 5
    },
    "cache": {
      "name": "cache",
      "status": "unhealthy",
      "error": "connection refused",
      "latency_ms": 100
    }
  }
}

HTTP Status Codes

Health Status HTTP Status Code
ok 200 OK
degraded 200 OK
unhealthy 503 Service Unavailable
disabled N/A (skipped in aggregation)

Requirements

  • Go 1.26 or later
  • github.com/gofiber/fiber/v2 v2.52.6+ (for Fiber handlers)
  • github.com/redis/go-redis/v9 v9.7.3+ (for Redis probe)

Test Coverage

Run tests:

go test ./... -v

# With coverage
go test ./... -coverprofile=coverage.out -covermode=atomic
go tool cover -html=coverage.out -o coverage.html
go tool cover -func=coverage.out

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

See LICENSE file for details.

Documentation

Overview

Package health provides a unified health check toolkit for Go services. It includes health check interfaces, probe implementations, multi-probe aggregation, and HTTP handlers compatible with both Fiber and net/http.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FiberHandler

func FiberHandler(aggregator *Aggregator) fiber.Handler

FiberHandler returns a Fiber handler for health checks

func FiberLivenessHandler

func FiberLivenessHandler(serviceName string) fiber.Handler

FiberLivenessHandler returns a simple Fiber liveness check handler

func FiberReadinessHandler

func FiberReadinessHandler(aggregator *Aggregator) fiber.Handler

FiberReadinessHandler returns a Fiber readiness check handler

func HTTPStatusCode

func HTTPStatusCode(status Status) int

HTTPStatusCode returns the appropriate HTTP status code for a health status

func Handler

func Handler(aggregator *Aggregator) http.HandlerFunc

Handler returns a standard library HTTP handler for health checks

func LivenessHandler

func LivenessHandler(serviceName string) http.HandlerFunc

LivenessHandler returns a simple liveness check handler (for Kubernetes) Always returns 200 OK if the service is running

func ReadinessHandler

func ReadinessHandler(aggregator *Aggregator) http.HandlerFunc

ReadinessHandler returns a readiness check handler (for Kubernetes) Returns 200 OK only if all critical checks pass

func SimpleFiberHandler

func SimpleFiberHandler(serviceName string) fiber.Handler

SimpleFiberHandler returns a minimal Fiber health check handler

func SimpleHandler

func SimpleHandler(serviceName string) http.HandlerFunc

SimpleHandler returns a minimal health check handler without aggregator Useful for simple services that just need to report they're running

Types

type AggregatedResult

type AggregatedResult struct {
	// Status is the overall health status
	Status Status `json:"status"`
	// Service is the name of the service
	Service string `json:"service"`
	// Checks contains individual check results
	Checks map[string]CheckResult `json:"checks,omitempty"`
	// Timestamp is when the aggregation was performed
	Timestamp time.Time `json:"timestamp"`
	// TotalLatency is the total time taken for all checks
	TotalLatency time.Duration `json:"-"`
}

AggregatedResult represents the combined result of multiple health checks

func (AggregatedResult) IsDegraded

func (r AggregatedResult) IsDegraded() bool

IsDegraded returns true if any check failed but service is still functional

func (AggregatedResult) IsHealthy

func (r AggregatedResult) IsHealthy() bool

IsHealthy returns true if the overall status is healthy

func (AggregatedResult) MarshalJSON

func (r AggregatedResult) MarshalJSON() ([]byte, error)

MarshalJSON customizes JSON marshaling for AggregatedResult

type Aggregator

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

Aggregator manages multiple health checkers and aggregates their results

func NewAggregator

func NewAggregator(config Config) *Aggregator

NewAggregator creates a new health check aggregator

func (*Aggregator) AddChecker

func (a *Aggregator) AddChecker(checker Checker) *Aggregator

AddChecker adds a health checker to the aggregator

func (*Aggregator) AddCheckers

func (a *Aggregator) AddCheckers(checkers ...Checker) *Aggregator

AddCheckers adds multiple health checkers to the aggregator

func (*Aggregator) Check

Check performs all health checks in parallel and aggregates the results

func (*Aggregator) CheckSequential

func (a *Aggregator) CheckSequential(ctx context.Context) AggregatedResult

CheckSequential performs all health checks sequentially Useful when parallel execution might cause issues

func (*Aggregator) Config

func (a *Aggregator) Config() Config

Config returns the current configuration

func (*Aggregator) GetCheckerNames

func (a *Aggregator) GetCheckerNames() []string

GetCheckerNames returns the names of all registered checkers

func (*Aggregator) RemoveChecker

func (a *Aggregator) RemoveChecker(name string) *Aggregator

RemoveChecker removes a checker by name

func (*Aggregator) SetConfig

func (a *Aggregator) SetConfig(config Config)

SetConfig updates the configuration

type CheckResult

type CheckResult struct {
	// Name is the identifier of the checked component
	Name string `json:"name"`
	// Status is the health status
	Status Status `json:"status"`
	// Latency is the time taken to perform the check
	Latency time.Duration `json:"-"`
	// Error contains error details if the check failed
	Error string `json:"error,omitempty"`
	// Message contains additional information
	Message string `json:"message,omitempty"`
	// Timestamp is when the check was performed
	Timestamp time.Time `json:"timestamp"`
	// Metadata contains additional check-specific data
	Metadata map[string]any `json:"metadata,omitempty"`
}

CheckResult represents the result of a single health check

func (CheckResult) MarshalJSON

func (r CheckResult) MarshalJSON() ([]byte, error)

MarshalJSON customizes JSON marshaling for CheckResult

type Checker

type Checker interface {
	// Name returns the name of the checker
	Name() string
	// Check performs the health check and returns the result
	Check(ctx context.Context) CheckResult
}

Checker is the interface that health check probes must implement

type CheckerFunc

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

CheckerFunc is a function adapter for Checker interface

func NewCheckerFunc

func NewCheckerFunc(name string, fn func(ctx context.Context) CheckResult) *CheckerFunc

NewCheckerFunc creates a new CheckerFunc with the given name and function

func (*CheckerFunc) Check

func (c *CheckerFunc) Check(ctx context.Context) CheckResult

Check performs the health check

func (*CheckerFunc) Name

func (c *CheckerFunc) Name() string

Name returns the name of the checker

type Config

type Config struct {
	// ServiceName is the name of the service for identification
	ServiceName string

	// Timeout is the default timeout for health checks
	Timeout time.Duration

	// IPWhitelist is a list of IP addresses/CIDRs allowed to access health endpoints
	// If empty, all IPs are allowed
	IPWhitelist []string

	// TrustedProxies is a list of proxy IPs/CIDRs that are allowed to supply
	// X-Forwarded-For or X-Real-IP headers.
	TrustedProxies []string

	// IncludeDetails controls whether to include detailed check results in response
	// Set to false in production to hide internal details
	IncludeDetails bool

	// IncludeChecks controls whether to include individual check results
	IncludeChecks bool

	// CriticalChecks is a list of check names that are critical
	// If any critical check fails, the overall status is unhealthy
	// Non-critical check failures result in degraded status
	CriticalChecks []string
	// contains filtered or unexported fields
}

Config holds the configuration for health check handlers

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults

func (*Config) IsCritical

func (c *Config) IsCritical(name string) bool

IsCritical checks if a check name is in the critical list

func (*Config) IsIPAllowed

func (c *Config) IsIPAllowed(ipStr string) bool

IsIPAllowed checks if the given IP is allowed by the whitelist Returns true if whitelist is empty (all IPs allowed)

func (*Config) IsTrustedProxy added in v1.1.0

func (c *Config) IsTrustedProxy(ipStr string) bool

IsTrustedProxy checks if the given IP belongs to a trusted proxy list

func (Config) WithChecks

func (c Config) WithChecks(include bool) Config

WithChecks sets whether to include individual checks

func (Config) WithCriticalChecks

func (c Config) WithCriticalChecks(checks []string) Config

WithCriticalChecks sets the list of critical checks

func (Config) WithDetails

func (c Config) WithDetails(include bool) Config

WithDetails sets whether to include details

func (Config) WithIPWhitelist

func (c Config) WithIPWhitelist(ips []string) Config

WithIPWhitelist sets the IP whitelist

func (Config) WithServiceName

func (c Config) WithServiceName(name string) Config

WithServiceName sets the service name

func (Config) WithTimeout

func (c Config) WithTimeout(timeout time.Duration) Config

WithTimeout sets the timeout

func (Config) WithTrustedProxies added in v1.1.0

func (c Config) WithTrustedProxies(ips []string) Config

WithTrustedProxies sets the trusted proxies list

type CustomChecker

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

CustomChecker allows creating health checkers from functions

func NewCustomChecker

func NewCustomChecker(name string, checkFn func(ctx context.Context) error) *CustomChecker

NewCustomChecker creates a new custom health checker

func (*CustomChecker) Check

func (c *CustomChecker) Check(ctx context.Context) CheckResult

Check performs the custom health check

func (*CustomChecker) Name

func (c *CustomChecker) Name() string

Name returns the checker name

func (*CustomChecker) WithMetadata

func (c *CustomChecker) WithMetadata(metadata map[string]any) *CustomChecker

WithMetadata sets static metadata for the check result

func (*CustomChecker) WithTimeout

func (c *CustomChecker) WithTimeout(timeout time.Duration) *CustomChecker

WithTimeout sets the timeout for custom checks

type DBChecker

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

DBChecker checks database connectivity

func NewDBChecker

func NewDBChecker(db *sql.DB) *DBChecker

NewDBChecker creates a new database health checker

func NewDBCheckerWithName

func NewDBCheckerWithName(name string, db *sql.DB) *DBChecker

NewDBCheckerWithName creates a new database health checker with custom name

func (*DBChecker) Check

func (c *DBChecker) Check(ctx context.Context) CheckResult

Check performs the database health check

func (*DBChecker) Name

func (c *DBChecker) Name() string

Name returns the checker name

func (*DBChecker) WithTimeout

func (c *DBChecker) WithTimeout(timeout time.Duration) *DBChecker

WithTimeout sets the timeout for database checks

type DisabledChecker

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

DisabledChecker always returns disabled status Useful for optional dependencies that are not configured

func NewDisabledChecker

func NewDisabledChecker(name string) *DisabledChecker

NewDisabledChecker creates a new disabled checker

func (*DisabledChecker) Check

Check returns a disabled status

func (*DisabledChecker) Name

func (c *DisabledChecker) Name() string

Name returns the checker name

func (*DisabledChecker) WithMessage

func (c *DisabledChecker) WithMessage(message string) *DisabledChecker

WithMessage sets the message for the disabled status

type HTTPChecker

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

HTTPChecker checks HTTP endpoint availability

func NewHTTPChecker

func NewHTTPChecker(name, url string) *HTTPChecker

NewHTTPChecker creates a new HTTP health checker

func (*HTTPChecker) Check

func (c *HTTPChecker) Check(ctx context.Context) CheckResult

Check performs the HTTP health check

func (*HTTPChecker) Name

func (c *HTTPChecker) Name() string

Name returns the checker name

func (*HTTPChecker) WithClient

func (c *HTTPChecker) WithClient(client *http.Client) *HTTPChecker

WithClient sets a custom HTTP client

func (*HTTPChecker) WithExpectedCode

func (c *HTTPChecker) WithExpectedCode(code int) *HTTPChecker

WithExpectedCode sets the expected HTTP status code

func (*HTTPChecker) WithMethod

func (c *HTTPChecker) WithMethod(method string) *HTTPChecker

WithMethod sets the HTTP method

func (*HTTPChecker) WithTimeout

func (c *HTTPChecker) WithTimeout(timeout time.Duration) *HTTPChecker

WithTimeout sets the timeout for HTTP checks

type RedisChecker

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

RedisChecker checks Redis connectivity

func NewRedisChecker

func NewRedisChecker(client *redis.Client) *RedisChecker

NewRedisChecker creates a new Redis health checker

func NewRedisCheckerWithName

func NewRedisCheckerWithName(name string, client *redis.Client) *RedisChecker

NewRedisCheckerWithName creates a new Redis health checker with custom name

func (*RedisChecker) Check

func (c *RedisChecker) Check(ctx context.Context) CheckResult

Check performs the Redis health check

func (*RedisChecker) Name

func (c *RedisChecker) Name() string

Name returns the checker name

func (*RedisChecker) WithTimeout

func (c *RedisChecker) WithTimeout(timeout time.Duration) *RedisChecker

WithTimeout sets the timeout for Redis checks

type Status

type Status string

Status represents the health status of a component

const (
	// StatusHealthy indicates the component is healthy
	StatusHealthy Status = "ok"
	// StatusUnhealthy indicates the component is unhealthy
	StatusUnhealthy Status = "unhealthy"
	// StatusDegraded indicates the component is partially healthy
	StatusDegraded Status = "degraded"
	// StatusDisabled indicates the component is disabled
	StatusDisabled Status = "disabled"
	// StatusUnknown indicates the component status cannot be determined
	StatusUnknown Status = "unknown"
)

func (Status) IsHealthy

func (s Status) IsHealthy() bool

IsHealthy returns true if the status indicates healthy state

func (Status) String

func (s Status) String() string

String returns the string representation of the status

Jump to

Keyboard shortcuts

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