package app import ( "context" "errors" "github.com/gofiber/fiber/v2/log" "github.com/themakers/hlog" "go.uber.org/zap" "go.uber.org/zap/zapcore" "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/initialize" "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/models" savewc "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/savewc" "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/server/http" "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/pkg/closer" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils" "penahub.gitlab.yandexcloud.net/external/trashlog/wrappers/zaptrashlog" "time" ) type Build struct { Commit string Version string } var zapOptions = []zap.Option{ zap.AddCaller(), zap.AddCallerSkip(2), zap.AddStacktrace(zap.ErrorLevel), } func New(ctx context.Context, cfg initialize.Config, build Build) error { var ( err error zapLogger *zap.Logger errChan = make(chan error) ) defer func() { if r := recover(); r != nil { log.Error("Recovered from a panic", zap.Any("error", r)) } }() ctx, cancel := context.WithCancel(ctx) defer cancel() if cfg.LoggerProdMode { zapLogger, err = zap.NewProduction(zapOptions...) if err != nil { return err } } else { zapLogger, err = zap.NewDevelopment(zapOptions...) if err != nil { return err } } zapLogger = zapLogger.With( zap.String("SvcCommit", build.Commit), zap.String("SvcVersion", build.Version), zap.String("SvcBuildTime", time.Now().String()), ) clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, cfg.TrashLogHost, build.Version, build.Commit, time.Now().Unix()) if err != nil { panic(err) } loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { return zapcore.NewTee(core, clickHouseLogger) })) loggerHlog := hlog.New(loggerForHlog).Module(cfg.ModuleLogger) loggerHlog.With(models.AllFields{}) loggerHlog.Emit(InfoSvcStarted{}) shutdownGroup := closer.NewCloserGroup() // Initialize minio client object. minioClient, err := initialize.NewMinio(cfg) if err != nil { zapLogger.Error("failed to initialize minio client", zap.Error(err)) return err } dalS, err := initialize.NewDALs(ctx, cfg, minioClient) if err != nil { zapLogger.Error("failed to initialize dals", zap.Error(err)) return err } redisClient, err := initialize.Redis(ctx, cfg) if err != nil { zapLogger.Error("failed to initialize redis client", zap.Error(err)) return err } encrypt := utils.NewEncrypt(cfg.PubKey, cfg.PrivKey) workerSendClientCh := make(chan model.Answer, 50) workerRespondentCh := make(chan []model.Answer, 50) saveRespWcData := savewc.DepsForResp{ WorkerRespondentCh: workerRespondentCh, Redis: redisClient, } saveClientWcData := savewc.DepsForClient{ WorkerSendClientCh: workerSendClientCh, Redis: redisClient, } saveRespWorker := savewc.NewSaveRespWorker(saveRespWcData, errChan, loggerHlog) saveClientWorker := savewc.NewSaveClientWorker(saveClientWcData, errChan, loggerHlog) go saveRespWorker.Start(ctx) go saveClientWorker.Start(ctx) controllers := initialize.NewControllers(initialize.ControllerDeps{ DALs: dalS, Config: cfg, RedisClient: redisClient, WorkerSendClientCh: workerSendClientCh, WorkerRespondentCh: workerRespondentCh, Encrypt: encrypt, }) srv := http.NewServer(http.ServerConfig{ Logger: zapLogger, Controllers: []http.Controller{controllers.HttpControllers.Common}, Hlogger: loggerHlog, }) go func() { if err := srv.Start(cfg.HttpHost + ":" + cfg.NumberPort); err != nil { zapLogger.Error("HTTP server startup error", zap.Error(err)) cancel() } }() srv.ListRoutes() loggerHlog.Emit(InfoSvcReady{}) shutdownGroup.Add(closer.CloserFunc(srv.Shutdown)) shutdownGroup.Add(closer.CloserFunc(dalS.PgDAL.Close)) <-ctx.Done() timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second) defer timeoutCancel() if err := shutdownGroup.Call(timeoutCtx); err != nil { if errors.Is(err, context.DeadlineExceeded) { zapLogger.Error("Shutdown timed out", zap.Error(err)) } else { zapLogger.Error("Failed to shutdown services gracefully", zap.Error(err)) } return err } zapLogger.Info("Application has stopped") return nil }