Skip to content

Health Monitor Package

Route health monitoring with configurable check intervals, retry policies, and notification integration.

Overview

Purpose

This package provides health monitoring for different route types in GoDoxy:

  • Monitors service health via configurable check functions
  • Tracks consecutive failures with configurable thresholds
  • Sends notifications on status changes
  • Provides last-seen tracking for idle detection

Primary Consumers

  • internal/route/ - Route health monitoring
  • internal/api/v1/metrics/ - Uptime poller integration
  • WebUI - Health status display

Non-goals

  • Health check execution itself (delegated to internal/health/check/)
  • Alert routing (handled by internal/notif/)
  • Automatic remediation

Stability

Internal package with stable public interfaces. HealthMonitor interface is stable.

Public API

Types

go
type HealthCheckFunc func(url *url.URL) (result types.HealthCheckResult, err error)

HealthMonitor Interface

go
type HealthMonitor interface {
    Start(parent task.Parent) error
    Task() *task.Task
    Finish(reason any)
    UpdateURL(url *url.URL)
    URL() *url.URL
    Config() *types.HealthCheckConfig
    Status() types.HealthStatus
    Uptime() time.Duration
    Latency() time.Duration
    Detail() string
    Name() string
    String() string
    CheckHealth() (types.HealthCheckResult, error)
}

Monitor Creation (new.go)

go
// Create monitor for agent-proxied routes
func NewAgentProxiedMonitor(
    ctx context.Context,
    cfg types.HealthCheckConfig,
    url *url.URL,
) (HealthMonitor, error)

// Create monitor for Docker containers
func NewDockerHealthMonitor(
    ctx context.Context,
    cfg types.HealthCheckConfig,
    url *url.URL,
    containerID string,
) (HealthMonitor, error)

// Create monitor for HTTP routes
func NewHTTPMonitor(
    ctx context.Context,
    cfg types.HealthCheckConfig,
    url *url.URL,
) HealthMonitor

// Create monitor for H2C (HTTP/2 cleartext) routes
func NewH2CMonitor(
    ctx context.Context,
    cfg types.HealthCheckConfig,
    url *url.URL,
) HealthMonitor

// Create monitor for file server routes
func NewFileServerMonitor(
    cfg types.HealthCheckConfig,
    url *url.URL,
) HealthMonitor

// Create monitor for stream routes
func NewStreamMonitor(
    cfg types.HealthCheckConfig,
    url *url.URL,
) HealthMonitor

// Unified monitor factory (routes to appropriate type)
func NewMonitor(
    ctx context.Context,
    cfg types.HealthCheckConfig,
    url *url.URL,
) (HealthMonitor, error)

Architecture

Monitor Selection Flow

Monitor State Machine

Component Structure

Configuration Surface

HealthCheckConfig

go
type HealthCheckConfig struct {
    Interval    time.Duration // Check interval (default: 30s)
    Timeout     time.Duration // Check timeout (default: 10s)
    Path        string        // Health check path
    Method      string        // HTTP method (GET/HEAD)
    Retries     int           // Consecutive failures before notification (-1 for immediate)
    BaseContext func() context.Context
}

Defaults

FieldDefault
Interval30s
Timeout10s
MethodGET
Path"/"
Retries3

Applying Defaults

go
cfg.ApplyDefaults(state.Value().Defaults.HealthCheck)

Dependency and Integration Map

Internal Dependencies

  • internal/task/task.go - Lifetime management
  • internal/notif/ - Status change notifications
  • internal/health/check/ - Health check implementations
  • internal/types/ - Health status types
  • internal/config/types/ - Working state

External Dependencies

  • github.com/puzpuzpuz/xsync/v4 - Atomic values

Observability

Logs

LevelWhen
InfoService comes up
WarnService goes down
ErrorHealth check error
ErrorMonitor stopped after 5 trials

Notifications

  • Service up notification (with latency)
  • Service down notification (with last seen time)
  • Immediate notification when Retries < 0

Metrics

  • Consecutive failure count
  • Last check latency
  • Monitor uptime

Failure Modes and Recovery

Failure ModeImpactRecovery
5 consecutive check errorsMonitor enters Error state, task stopsManual restart required
Health check function panicMonitor crashesAutomatic cleanup
Context cancellationMonitor stops gracefullyStopped state
URL update to invalidCheck will failManual URL fix

Status Transitions

FromToCondition
StartingHealthyCheck passes
StartingUnhealthyCheck fails
HealthyUnhealthyRetries consecutive failures
HealthyErrorCheck returns error
UnhealthyHealthyCheck passes
ErrorHealthyCheck passes

Usage Examples

Creating an HTTP Monitor

go
cfg := types.HealthCheckConfig{
    Interval: 15 * time.Second,
    Timeout: 5 * time.Second,
    Path:    "/health",
    Retries: 3,
}
url, _ := url.Parse("http://localhost:8080")

monitor := monitor.NewHTTPMonitor(context.Background(), cfg, url)
if err := monitor.Start(parent); err != nil {
    return err
}

// Check status
fmt.Printf("Status: %s\n", monitor.Status())
fmt.Printf("Latency: %v\n", monitor.Latency())

Creating a Docker Monitor

go
monitor, err := monitor.NewDockerHealthMonitor(
    context.Background(),
    cfg,
    url,
    containerID,
)
if err != nil {
    return err
}
monitor.Start(parent)

Unified Factory

go
monitor, err := monitor.NewMonitor(ctx, cfg, url)
if err != nil {
    return err
}
monitor.Start(parent)

Testing Notes

  • monitor_test.go - Monitor lifecycle tests
  • Mock health check functions for deterministic testing
  • Status transition coverage tests
  • Notification trigger tests

Released under the MIT License.