check / check (push) Successful in 3m40s
fx wrote its own steps of starting and stopping as plain text to stderr. It now logs them with its own slog event logger through the backend's logger, so off a terminal every line from start to stop is JSON. A config file that is found but cannot be read now makes config.New return the error instead of panicking. A test runs the server as a child process and checks that all it writes is JSON, on a normal start and stop and with such a config file. The backend logs its name, version and architecture once at start. The health check's uptime keys are now uptime_seconds and uptime_human, and its type and method take the names GO_HTTP_SERVER_CONVENTIONS.md gives. Its path, content type, status and code are unchanged. Model: opus-5-5
86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
// Package healthcheck provides a service that reports
|
|
// application health, uptime, and version information.
|
|
package healthcheck
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"sneak.berlin/go/netwatch/internal/config"
|
|
"sneak.berlin/go/netwatch/internal/globals"
|
|
"sneak.berlin/go/netwatch/internal/logger"
|
|
|
|
"go.uber.org/fx"
|
|
)
|
|
|
|
// Params defines the dependencies for Healthcheck.
|
|
type Params struct {
|
|
fx.In
|
|
|
|
Config *config.Config
|
|
Globals *globals.Globals
|
|
Logger *logger.Logger
|
|
}
|
|
|
|
// Healthcheck tracks startup time and builds health responses.
|
|
type Healthcheck struct {
|
|
StartupTime time.Time
|
|
log *slog.Logger
|
|
params *Params
|
|
}
|
|
|
|
// HealthcheckResponse is the JSON payload returned by the health
|
|
// check endpoint. Its name and its snake_case keys are the ones
|
|
// GO_HTTP_SERVER_CONVENTIONS.md gives.
|
|
//
|
|
//nolint:revive,tagliatelle // name and keys from the conventions
|
|
type HealthcheckResponse struct {
|
|
Appname string `json:"appname"`
|
|
Now string `json:"now"`
|
|
Status string `json:"status"`
|
|
UptimeHuman string `json:"uptime_human"`
|
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
// New creates a Healthcheck, recording startup time via an
|
|
// fx lifecycle hook.
|
|
func New(
|
|
lc fx.Lifecycle,
|
|
params Params,
|
|
) (*Healthcheck, error) {
|
|
s := new(Healthcheck)
|
|
s.params = ¶ms
|
|
s.log = params.Logger.Get()
|
|
|
|
lc.Append(fx.Hook{
|
|
OnStart: func(_ context.Context) error {
|
|
s.StartupTime = time.Now().UTC()
|
|
|
|
return nil
|
|
},
|
|
OnStop: func(_ context.Context) error {
|
|
return nil
|
|
},
|
|
})
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// Healthcheck returns the current health status of the application.
|
|
func (s *Healthcheck) Healthcheck() *HealthcheckResponse {
|
|
return &HealthcheckResponse{
|
|
Appname: s.params.Globals.Appname,
|
|
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
|
Status: "ok",
|
|
UptimeHuman: s.uptime().String(),
|
|
UptimeSeconds: int64(s.uptime().Seconds()),
|
|
Version: s.params.Globals.Version,
|
|
}
|
|
}
|
|
|
|
func (s *Healthcheck) uptime() time.Duration {
|
|
return time.Since(s.StartupTime)
|
|
}
|