This document serves as the central reference for configuring lifecycle components.
We use the Functional Options Pattern to provide flexible and readable configuration.
lifecycle.Run)Configures how the application reacts to OS signals (Shutdown Management) and coordinates the root context.
lifecycle.Run(job,
lifecycle.WithForceExit(2), // Force exit after 2nd signal
lifecycle.WithResetTimeout(1*time.Second), // Reset signal count after 1s
lifecycle.WithHookTimeout(5*time.Second), // Max time for cleanup hooks
)
| Option | Default | Description |
|---|---|---|
WithForceExit(count int) |
1 |
Number of signals required to force os.Exit(1). • 1: Instant shutdown on first signal.• >1: Escalation mode (e.g. 2 = first signal cancels context, second forces exit).• 0: Unsafe mode (never force exits). |
WithResetTimeout(d time.Duration) |
0 (Disabled) |
Duration after which the internal signal counter resets. Useful to prevent accidental force exits from spaced-out signals. |
WithHookTimeout(d time.Duration) |
5s |
Maximum duration to wait for a single OnShutdown hook before logging a warning. Does not abort the hook, only warns. |
WithCancelOnInterrupt(enabled bool) |
true |
If true, the context is cancelled immediately on the first signal. If false, the context remains valid until ForceExit threshold is reached or manual cancellation. |
WithLogger(l *slog.Logger) |
slog.Default() |
Sets the global logger for the runtime. |
WithMetrics(p metrics.Provider) |
NoOp |
Sets the global metrics provider. |
lifecycle.NewInteractiveRouter)Pre-configured router for CLI applications with built-in signal and input handling.
router := lifecycle.NewInteractiveRouter(suspendHandler,
lifecycle.WithShutdown(func() { ... }),
lifecycle.WithCommand("status", statusHandler),
)
| Option | Default | Description |
|---|---|---|
WithInput(bool) |
true |
Enables/Disables reading commands from Stdin. |
WithSignal(bool) |
true |
Enables/Disables OS signal handling (Interrupt/Term). |
WithCommand(name, handler) |
- | Registers a custom command (e.g., command/status). |
WithShutdown(func()) |
No-Op |
Convenience to handle q/quit commands. |
WithInputBackoff(d) |
100ms |
Duration to wait before retrying after interrupts or errors. |
WithInputBufferSize(size) |
1024 |
Size of the internal I/O read buffer (in bytes). |
WithInputEventBuffer(size) |
10 |
Size of the event channel buffer. |
lifecycle.NewRouter)Configures the Event Bus for internal communication.
router := lifecycle.NewRouter(
lifecycle.WithEventBuffer(500),
)
| Option | Default | Description |
|---|---|---|
WithEventBuffer(size int) |
100 |
Size of the event channel buffer. Larger buffers handle bursty traffic better but consume more memory. |
lifecycle.NewSupervisor)Configures the supervision tree for managing child workers.
sup := lifecycle.NewSupervisor("root", lifecycle.SupervisorStrategyOneForOne,
lifecycle.SupervisorSpec{
Name: "worker-1",
Factory: myWorkerFactory,
RestartPolicy: lifecycle.RestartAlways,
Backoff: lifecycle.Backoff{Min: 1*time.Second, Max: 5*time.Second},
},
)
| Strategy | Description |
|---|---|
SupervisorStrategyOneForOne |
If a child process terminates, only that process is restarted. |
SupervisorStrategyOneForAll |
If a child process terminates, all other child processes are terminated and then restarted. |
| Policy | Description |
|---|---|
RestartAlways |
Always restart the worker, regardless of exit reason. |
RestartOnFailure |
Restart only if the worker returns an error. |
RestartNever |
Never restart. |
Configures event sources for health checks, webhooks, and file watching.
lifecycle.NewHealthCheckSource)source := lifecycle.NewHealthCheckSource("db", checkFunc,
lifecycle.WithHealthInterval(10*time.Second),
lifecycle.WithHealthStrategy(lifecycle.TriggerLevel),
)
| Option | Default | Description |
|---|---|---|
WithHealthInterval(d time.Duration) |
30s |
How often to run the check function. |
WithHealthStrategy(s TriggerStrategy) |
TriggerEdge |
TriggerEdge (Emit only on status change) or TriggerLevel (Emit event on every check). |
lifecycle.NewWebhookSource)source := lifecycle.NewWebhookSource(":8080",
lifecycle.WithWebhookBuffer(50), // Note: Check pkg/events specific options if needed
)
These settings affect the entire library state.
| Function | Description |
|---|---|
lifecycle.SetLogger(l *slog.Logger) |
Overrides the default logger used by all components if not explicitly provided. |
lifecycle.NewNoOpLogger() |
Returns a logger that discards all output (use with WithLogger). |
lifecycle.SetObserver(o lifecycle.Observer) |
Routes logs, process events, and panic callbacks to a custom observer (disables default slog when set). |
lifecycle.SetMetricsProvider(p) |
Connects lifecycle internal metrics to your observability backend (Prometheus, OTel). |
lifecycle.SetMetricsLabelPolicy(p *metrics.LabelPolicy) |
Sanitizes metric labels and enforces cardinality rules. |
[!NOTE] Passing
niltoSetLoggerresets to the default logger — it does not silence logs. To disable logging entirely, useNewNoOpLogger().
lifecycle.Run(job, lifecycle.WithLogger(lifecycle.NewNoOpLogger()))
When using both lifecycle workers and procio processes, you can unify telemetry with a single adapter that implements both Observer interfaces.
[!NOTE] The observer affects log routing, process events, and panic callbacks. Metrics are configured independently via
SetMetricsProvider. For behavior and stack capture semantics, see docs/TECHNICAL.md.
Type definition:
import (
"log/slog"
"github.com/aretw0/lifecycle"
"github.com/aretw0/lifecycle/pkg/core/metrics"
"github.com/aretw0/procio"
)
// ObserverBridge implements both lifecycle.Observer and procio.Observer,
// routing logs to slog and process events to the metrics provider.
type ObserverBridge struct {
Logger *slog.Logger
Provider metrics.Provider
}
// Compile-time interface checks.
var _ lifecycle.Observer = (*ObserverBridge)(nil)
var _ procio.Observer = (*ObserverBridge)(nil)
func (b *ObserverBridge) OnProcessStarted(pid int) {
b.Logger.Info("process started", "pid", pid)
b.Provider.IncProcessStarted()
// Alternative: metrics.GetProvider().IncProcessStarted()
}
func (b *ObserverBridge) OnProcessFailed(err error) {
b.Logger.Error("process failed", "error", err)
b.Provider.IncProcessFailed()
}
func (b *ObserverBridge) OnGoroutinePanicked(recovered any, stack []byte) {
if len(stack) == 0 {
b.Logger.Error("goroutine panicked", "recover", recovered)
return
}
// Only log stack when provided by the runtime.
b.Logger.Error("goroutine panicked", "recover", recovered, "stack", string(stack))
}
func (b *ObserverBridge) LogDebug(msg string, args ...any) { b.Logger.Debug(msg, args...) }
func (b *ObserverBridge) LogInfo(msg string, args ...any) { b.Logger.Info(msg, args...) }
func (b *ObserverBridge) LogWarn(msg string, args ...any) { b.Logger.Warn(msg, args...) }
func (b *ObserverBridge) LogError(msg string, args ...any) { b.Logger.Error(msg, args...) }
Setup:
bridge := &ObserverBridge{
Logger: slog.Default(),
Provider: myMetricsProvider, // or metrics.GetProvider()
}
lifecycle.SetObserver(bridge)
procio.SetObserver(bridge)