Files
app/README.md
2026-01-27 15:03:05 -08:00

1.8 KiB

app

app is a simple and lightweight app lifecycle management library.

Example

Keep your main.go as simple as possible, all it's responsible for is creating the lifecycle, defining a default logger, and orchestrating setup and teardown.

main.go

package main

import (
	"context"
	"fmt"
	"log/slog"

	"gitea.auvem.com/go-toolkit/app"
)

func main() {
	// Create a new lifecycle, defaulting to no printed logs
	lifecycle := app.NewLifecycle().WithLogger(slog.New(slog.DiscardHandler))
	defer func() {
		if err := lifecycle.Teardown(); err != nil {
			fmt.Println("Error during shutdown", err)
		}
	}()

	// Encodes the lifecycle into a context to be used downstream
	ctx := lifecycle.Context(context.Background())

	// Off to you, call your entrypoint here.
	Hello(ctx)
}

Now, let's define a basic logging module and handle loading that module.

logger.go

package main

import (
	"log/slog"

	"gitea.auvem.com/go-toolkit/app"
)

func ModuleLog(setDefault bool) *app.Module {
	return app.NewModule("logger", app.ModuleOpts{
		Setup: func(m *app.Module) error {
			handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})
			if setDefault {
				slog.SetDefault(handler)
			}

			m.Lifecycle().WithLogger(handler)
		}
	})
}

Logging can, however, be somewhat of a complex operation in itself, so I'd highly recommend checking out my applog project, which features tight integration with this app.

Finally, lets put our new lifecycle (and logger) to work!

hello.go

package main

import (
	"context"

	"gitea.auvem.com/go-toolkit/app"
)

func Hello(ctx context.Context) {
	app := app.LifecycleFromContext(ctx)
	if app == nil {
		panic("hello must run within an app lifecycle")
	}
	lifecycle.Require(ModuleLog(true))

	app.Logger().Info("Hello world!", "foo", "bar")
}