package fairwind import ( "context" "os" "os/signal" "sync" "syscall" ) type ApplicationModule interface { Name() string Start() error Stop() error } type ApplicationModuleBuilder func(ctx context.Context, log *Log) (ApplicationModule, error) type Application struct { ctx context.Context cancel func() log *Log modules []ApplicationModule } func Run(builders ...ApplicationModuleBuilder) { ctx, cancel := context.WithCancel(context.Background()) log := NewLog(NewLogFormatterJSON()) modules := []ApplicationModule{} for _, builder := range builders { module, err := builder(ctx, log) if err != nil { log.Error("can't build module", LogValue("name", module.Name()), LogError(err)) cancel() return } modules = append(modules, module) } this := &Application{ ctx: ctx, cancel: cancel, log: log, modules: modules, } this.log.Information("starting application") this.startup() channel := make(chan os.Signal, 1) signal.Notify(channel, syscall.SIGTERM, syscall.SIGINT) <-channel this.log.Information("stopping application") this.cancel() this.shutdown() } func (this *Application) startup() { waitGroup := sync.WaitGroup{} waitGroup.Add(len(this.modules)) start := func(log *Log, module ApplicationModule) { log.Information("starting module", LogValue("module", module.Name())) err := module.Start() if err != nil { log.Error("error starting module", LogValue("module", module.Name()), LogError(err)) } else { log.Information("module started", LogValue("module", module.Name())) } waitGroup.Done() } for _, module := range this.modules { go start(this.log, module) } waitGroup.Wait() } func (this *Application) shutdown() { waitGroup := sync.WaitGroup{} waitGroup.Add(len(this.modules)) stop := func(log *Log, module ApplicationModule) { log.Information("stopping module", LogValue("module", module.Name())) err := module.Stop() if err != nil { log.Error("error stopping module", LogValue("module", module.Name()), LogError(err)) } else { log.Information("module stopped", LogValue("module", module.Name())) } waitGroup.Done() } for _, module := range this.modules { go stop(this.log, module) } waitGroup.Wait() }