Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8d6930b087
|
16
CHANGELOG.md
16
CHANGELOG.md
@@ -5,6 +5,22 @@ All notable changes to this module will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0] — 2026-05-12
|
||||
|
||||
### Added
|
||||
|
||||
- `Config` struct — `CheckTimeout time.Duration`; zero value defaults to 5 seconds.
|
||||
- `NewHandlerWithConfig(logger Logger, cfg Config, checks ...Checkable) http.Handler` —
|
||||
constructor with explicit configuration. `NewHandler` is now a backward-compatible
|
||||
wrapper that calls `NewHandlerWithConfig` with zero `Config`.
|
||||
|
||||
### Unchanged
|
||||
|
||||
All existing API (`Level`, `LevelCritical`, `LevelDegraded`, `Checkable`, `Logger`,
|
||||
`ComponentStatus`, `Response`, `NewHandler`) is API-compatible with v0.9.0.
|
||||
|
||||
[1.0.0]: https://code.nochebuena.dev/go/health/releases/tag/v1.0.0
|
||||
|
||||
## [0.9.0] - 2026-03-18
|
||||
|
||||
### Added
|
||||
|
||||
27
health.go
27
health.go
@@ -48,23 +48,44 @@ type Response struct {
|
||||
Components map[string]ComponentStatus `json:"components"`
|
||||
}
|
||||
|
||||
// Config configures a health handler.
|
||||
// The zero value is valid: 5-second check timeout.
|
||||
type Config struct {
|
||||
// CheckTimeout is the per-request deadline for all health checks.
|
||||
// Defaults to 5 seconds when zero.
|
||||
CheckTimeout time.Duration
|
||||
}
|
||||
|
||||
const defaultCheckTimeout = 5 * time.Second
|
||||
|
||||
type handler struct {
|
||||
logger Logger
|
||||
checks []Checkable
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewHandler returns an http.Handler for the health endpoint.
|
||||
// NewHandler returns an http.Handler for the health endpoint with default configuration.
|
||||
// Runs all checks concurrently with a 5-second timeout.
|
||||
// Returns 200 (UP/DEGRADED) or 503 (DOWN).
|
||||
func NewHandler(logger Logger, checks ...Checkable) http.Handler {
|
||||
return &handler{logger: logger, checks: checks}
|
||||
return NewHandlerWithConfig(logger, Config{}, checks...)
|
||||
}
|
||||
|
||||
// NewHandlerWithConfig returns an http.Handler configured by cfg.
|
||||
// If cfg.CheckTimeout is zero, a 5-second default is used.
|
||||
func NewHandlerWithConfig(logger Logger, cfg Config, checks ...Checkable) http.Handler {
|
||||
timeout := cfg.CheckTimeout
|
||||
if timeout == 0 {
|
||||
timeout = defaultCheckTimeout
|
||||
}
|
||||
return &handler{logger: logger, checks: checks, timeout: timeout}
|
||||
}
|
||||
|
||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
logger := h.logger.WithContext(r.Context())
|
||||
logger.Debug("health: running checks")
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(r.Context(), h.timeout)
|
||||
defer cancel()
|
||||
|
||||
type result struct {
|
||||
|
||||
@@ -167,6 +167,30 @@ func TestHandler_JSON_Shape(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerWithConfig_CustomTimeout(t *testing.T) {
|
||||
// 200ms delay check with a 50ms timeout — should be reported as DOWN.
|
||||
h := NewHandlerWithConfig(&noopLogger{}, Config{CheckTimeout: 50 * time.Millisecond},
|
||||
&mockCheck{name: "slow", priority: LevelCritical, delay: 200 * time.Millisecond},
|
||||
)
|
||||
code, resp := doRequest(t, h)
|
||||
if code != http.StatusServiceUnavailable {
|
||||
t.Errorf("want 503, got %d", code)
|
||||
}
|
||||
if resp.Status != "DOWN" {
|
||||
t.Errorf("want DOWN, got %s", resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerWithConfig_ZeroTimeout_UsesDefault(t *testing.T) {
|
||||
h := NewHandlerWithConfig(&noopLogger{}, Config{},
|
||||
&mockCheck{name: "db", priority: LevelCritical},
|
||||
)
|
||||
code, resp := doRequest(t, h)
|
||||
if code != http.StatusOK || resp.Status != "UP" {
|
||||
t.Errorf("want 200 UP, got %d %s", code, resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_ContextTimeout(t *testing.T) {
|
||||
// Check that times out faster than the 5s global timeout when client cancels.
|
||||
h := NewHandler(&noopLogger{},
|
||||
|
||||
Reference in New Issue
Block a user