// This template contains a simple // app with OTEL bootstrap that will create an // HTTP server configured by environment that exports // spans and metrics to an OTEL collector if configured // to do so. Will also stand up a prometheus metrics // endpoint. // // Configuration and logger stored in context // Reference implementation of the provided packages package main import ( "context" "flag" "fmt" "os" "os/signal" "sync" "time" "github.com/rs/zerolog" "golang.org/x/sys/unix" "gitea.libretechconsulting.com/rmcguire/go-app/pkg/app" "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/config" "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/demo" "gitea.libretechconsulting.com/rmcguire/go-server-with-otel/pkg/service" ) const terminationGracePeriod = 30 * time.Second var ( // TODO: Register your service.AppService implementers here // This should be the only change necessary here // NOTE: At least one service needs to add a dial option // for transport credentials, otherwise you will have to do it here appServices = []service.AppService{ &demo.DemoService{}, } flagSchema bool ) func main() { ctx, cncl := signal.NotifyContext(context.Background(), os.Interrupt, unix.SIGTERM) defer cncl() // Load configuration and setup logging. The go-app framework // will handle loading config and environment into our demo // app config struct which embeds app.AooConfig ctx, serviceConfig := app.MustLoadConfigInto(ctx, &config.ServiceConfig{}) log := zerolog.Ctx(ctx) // Print schema if that's all we have to do if flagSchema { printSchema() os.Exit(1) } log.Debug().Any("mergedConfig", serviceConfig).Msg("App configuration prepared") // Prepare services var err error sdFuncs := make([]service.ShutdownFunc, len(appServices)) for i, svc := range appServices { sdFuncs[i], err = svc.Init(ctx, serviceConfig) if err != nil { log.Fatal().Err(err).Send() } } // Merge all service implementations for app grpcSvcs, httpSvcs := service.MergeServices(ctx, appServices...) // Prepare app with all merged services app := &app.App{ AppContext: ctx, GRPC: grpcSvcs, HTTP: httpSvcs, } // Launch app app.MustRun() // Wait for app to complete and then perform internal shutdown <-app.Done() log.Info().Int("numServices", len(appServices)).Msg("shutting down app services") sdCtx, cncl := context.WithTimeout(context.Background(), terminationGracePeriod) defer cncl() // Call registered shutdown funcs from all services var sdWg sync.WaitGroup for _, sdFunc := range sdFuncs { sdWg.Add(1) go func() { defer sdWg.Done() svcName, err := sdFunc(sdCtx) log.Err(err).Str("service", svcName).Msg("terminated") }() } sdWg.Wait() } // flag.Parse will be called by go-app func init() { flag.BoolVar(&flagSchema, "schema", false, "generate json schema and exit") } func printSchema() { bytes, err := app.CustomSchema(&config.ServiceConfig{}) if err != nil { panic(err) } fmt.Println(string(bytes)) os.Exit(0) }