Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/bootstrap"
Package bootstrap wires together the core infrastructure of a Go service: context lifecycle, structured logging, metrics collection, OS signal handling, and graceful shutdown, in a single function call.
How It Works
The entry point is Bootstrap. It accepts a BindFunc, the caller-supplied
function that wires up all application-specific components, plus a variadic
list of Option values that tune the runtime behavior:
- A cancellable
context.Contextis created and threaded through the entire application via BindFunc. - A
metrics.Clientis created and passed to BindFunc. The default ismetrics.Default, the no-op client, so bootstrap pulls in no metrics backend of its own;WithCreateMetricsClientFuncsupplies a real one. - A [*slog.Logger] is created and passed to BindFunc.
If a
logutil.Configis provided withWithLogConfig, the logger emits a metrics counter for every log line, broken down by level. - BindFunc is called. This is where the caller registers HTTP servers, database connections, background workers, etc.
- Bootstrap blocks until it receives os.Interrupt (SIGINT), SIGTERM, or until the context is canceled externally.
- A shutdown signal is broadcast on the shared channel
(see
WithShutdownSignalChan) and the application context is canceled, so every registered dependent (whether keyed on the channel or on ctx.Done()) can start its own teardown. - Bootstrap waits for all dependants to finish via a
sync.WaitGroup(seeWithShutdownWaitGroup), bounded by a configurable timeout (seeWithShutdownTimeout) to prevent hanging indefinitely. If the timeout fires first, Bootstrap returns an error wrappingErrShutdownTimeout. - The metrics client is closed so buffered measurements are flushed before the process exits.
Notes
Bootstrap installs process-global OS signal handling via os/signal.Notify, so
it is intended to be called once per process (for example from main) and not
concurrently with another Bootstrap call in the same process. Supplying a
logutil.Config with WithLogConfig also replaces the process-wide default
logger (via slog.SetDefault) and redirects the standard library log package.
Usage
Wire your application in a BindFunc and pass it to [Bootstrap]:
func bind(ctx context.Context, l *slog.Logger, m metrics.Client) error {
// register HTTP servers, workers, DB connections, etc.
return nil
}
func main() {
shutdownWG := &sync.WaitGroup{}
shutdownCh := make(chan struct{})
err := bootstrap.Bootstrap(
bind,
bootstrap.WithLogConfig(logutil.DefaultConfig()),
bootstrap.WithShutdownTimeout(30*time.Second),
bootstrap.WithShutdownWaitGroup(shutdownWG),
bootstrap.WithShutdownSignalChan(shutdownCh),
)
if err != nil {
log.Fatal(err)
}
}
For a complete, runnable implementation see, in order:
- examples/service/cmd/main.go
- examples/service/internal/cli/cli.go
- examples/service/internal/cli/bind.go
When To Use
- A service’s main should be short and the lifecycle wiring consistent across services.
- Shutdown must wait for background workers, bounded by a timeout.
- Log records should increment a metrics counter per level.
Example
// A real service lets Bootstrap block on SIGINT or SIGTERM. The example
// cancels the context from inside the bind function so it terminates.
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
var wg sync.WaitGroup
shutdown := make(chan struct{})
// BindFunc is where the application registers its own components: HTTP
// servers, database connections, background workers. It receives the
// application context, logger, and metrics client already wired.
bindFn := func(ctx context.Context, _ *slog.Logger, mtr metrics.Client) error {
worker(ctx, &wg, shutdown)
mtr.IncErrorCounter("startup", "bind", "0")
fmt.Println("application wired")
// Stand-in for the signal that would normally end the process.
cancel()
return nil
}
// Bootstrap returns once every registered dependant has finished, or
// with an error wrapping ErrShutdownTimeout if they exceed the budget.
err := bootstrap.Bootstrap(
bindFn,
bootstrap.WithContext(ctx),
bootstrap.WithShutdownSignalChan(shutdown),
bootstrap.WithShutdownWaitGroup(&wg),
bootstrap.WithShutdownTimeout(5*time.Second),
bootstrap.WithCreateMetricsClientFunc(func() (metrics.Client, error) {
return &metrics.Default{}, nil
}),
)
fmt.Println("bootstrap returned:", err)
// Output:
// application wired
// worker stopped
// bootstrap returned: <nil>
Full source is in example_bootstrap_test.go. More runnable examples are on pkg.go.dev.
Dependencies
Importing this package pulls 4 external modules:
github.com/mattn/go-colorablegithub.com/mattn/go-isattygithub.com/rs/zerologgolang.org/x/sys