package main import ( "fmt" "net/http" _ "net/http/pprof" "os" "os/signal" "syscall" "go.uber.org/zap" "go.c3c.cz/cv/app/server/internal/config" "go.c3c.cz/cv/app/server/internal/httpserver" "go.c3c.cz/cv/app/server/internal/pprofserver" "go.c3c.cz/cv/app/server/internal/version" ) func main() { defer handleExit() notice := "Starting version " if version.Tag != "" { notice += version.Tag } else { notice += "latest" } if version.Commit != "" { notice = fmt.Sprintf("%s (%s)", notice, version.Commit) } var logger *zap.Logger var err error logger, err = config.LoggerConfig.Build() if err != nil { fmt.Println(notice) fmt.Println(version.CommitTime.Format("Committed at 2006/01/02 15:04:05 MST")) panic(Exit{fmt.Errorf("logger creation failed")}) } logger.Info( "App initializing", zap.String("version", version.Tag), zap.String("commit", version.Commit), zap.Time("commitTime", version.CommitTime), ) defer func() { _ = logger.Sync() }() httpSrv, err := httpserver.New(&httpserver.Options{ BindAddr: config.BindAddrHttp, FrontendConfig: config.Frontend, Logger: logger, }) if err != nil { logger.Error("HTTP server creation failed", zap.Error(err)) panic(Exit{err}) } quit := struct { os chan os.Signal pprof chan error http chan error }{ os: make(chan os.Signal, 1), pprof: make(chan error, 1), http: make(chan error, 1), } pprofSrv := pprofserver.New(pprofserver.Options{ BindAddr: config.BindAddrPprof, Logger: logger, LogLevelHandler: config.LoggerConfig.Level, }) // Pprof server. go func() { logger.Info("debug server was started", zap.String("BindAddr", config.BindAddrPprof)) quit.pprof <- pprofSrv.ListenAndServe() }() go func() { logger.Info("http server was started", zap.String("BindAddr", config.BindAddrHttp)) pprofSrv.SetReady() err := httpSrv.ListenAndServe() if err != http.ErrServerClosed { quit.http <- err } }() logger.Info("application started") signal.Notify(quit.os, os.Interrupt, syscall.SIGTERM) select { case s := <-quit.os: sig, _ := s.(syscall.Signal) logger.Info("Signal received\n", zap.Int("signal", int(sig))) pprofSrv.SetNotReady() httpSrv.ShutdownWithDelay(config.GraceTerminationDelay, config.GraceTerminationLimit) case err := <-quit.pprof: logger.Error("app shutdown was triggered by a pprof server error", zap.Error(err)) httpSrv.ShutdownWithDelay(config.GraceTerminationDelay, config.GraceTerminationLimit) case err := <-quit.http: logger.Error("app shutdown was triggered by an http server error", zap.Error(err)) } } type Exit struct { err error } func handleExit() { if e := recover(); e != nil { if exit, ok := e.(Exit); ok { fmt.Println(exit.err) os.Exit(1) } panic(e) } }