func Main()

in router/cmd/main.go [38:231]


func Main() {
	_ = godotenv.Load()
	_ = godotenv.Load(".env.local")

	// Parse flags before calling profile.Start(), since it may add flags
	flag.Parse()

	if *help {
		flag.PrintDefaults()
		os.Exit(0)
	} else if *routerVersion {
		bi := versioninfo.New(core.Version, core.Commit, core.Date)
		fmt.Println(bi.String())
		os.Exit(0)
	}

	// We load this after flag parse so that "OVERRIDE_ENV" can be set by dotenv OR the flag
	if *overrideEnvFlag != "" {
		_ = godotenv.Overload(*overrideEnvFlag)
	}

	/*
		Config path precedence:
		1. Flag
		2. Environment variable
		3. Dotenv loaded environment variable
		4. Default config file
	*/

	// If not set by flag or normal environment variable, check again for dotenv override loaded envar
	if len(*configPathFlag) == 0 {
		configPathEnv := os.Getenv("CONFIG_PATH")
		err := configPathFlag.Set(configPathEnv)
		if err != nil {
			// This should be unreachable unless someone returns an non nil err
			log.Fatalf("Could not set config path from environment variable: %s", err)
		}
	}

	// If it is still not set, default to config paths
	if len(*configPathFlag) == 0 {
		*configPathFlag = multipleString{config.DefaultConfigPath}
	}

	result, err := config.LoadConfig(*configPathFlag)
	if err != nil {
		log.Fatalf("Could not load config: %s", err)
	}

	logLevelAtomic := zap.NewAtomicLevelAt(result.Config.LogLevel)

	baseLogger := logging.New(!result.Config.JSONLog, result.Config.DevelopmentMode, logLevelAtomic).
		With(
			zap.String("service", "@wundergraph/router"),
			zap.String("service_version", core.Version),
		)

	// Start pprof server if address is provided
	if *pprofListenAddr != "" {
		pprofSvr := profile.NewServer(*pprofListenAddr, baseLogger)
		defer pprofSvr.Close()
		go pprofSvr.Listen()
	}

	// Start profiling if flags are set
	profiler := profile.Start(baseLogger, *cpuProfilePath, *memProfilePath)
	defer profiler.Finish()

	rs, err := core.NewRouterSupervisor(&core.RouterSupervisorOpts{
		BaseLogger: baseLogger,
		ConfigFactory: func() (*config.Config, error) {
			result, err := config.LoadConfig(*configPathFlag)
			if err != nil {
				return nil, fmt.Errorf("could not load config: %w", err)
			}

			if !result.DefaultLoaded {
				baseLogger.Info(
					"Config file provided. Values in the config file have higher priority than environment variables",
					zap.Strings("config_file", *configPathFlag),
				)
			}

			logLevelAtomic.SetLevel(result.Config.LogLevel)

			return &result.Config, nil
		},
	})
	if err != nil {
		log.Fatalf("Could not create router supervisor: %s", err)
	}

	rootCtx, rootCancel := context.WithCancel(context.Background())
	defer rootCancel()

	// Handling shutdown signals
	{
		killChan := make(chan os.Signal, 1)

		signal.Notify(killChan, os.Interrupt,
			syscall.SIGTERM, // default for kill
			syscall.SIGQUIT, // ctrl + \
			syscall.SIGINT,  // ctrl+c
		)

		go func() {
			select {
			case <-rootCtx.Done():
				return
			case <-killChan:
				rs.Stop()
			}
		}()
	}

	// Handling reload signal
	{
		reloadChan := make(chan os.Signal, 1)

		signal.Notify(reloadChan, syscall.SIGHUP)

		go func() {
			for {
				select {
				case <-rootCtx.Done():
					return
				case <-reloadChan:
					rs.Reload()
				}
			}
		}()
	}

	// Setup config file watcher if enabled
	if result.Config.WatchConfig.Enabled {
		ll := baseLogger.With(zap.String("watcher_label", "router_config"))

		startupDelay := 0 * time.Second

		// Apply startup delay if configured
		if result.Config.WatchConfig.StartupDelay.Enabled {
			startupDelay = timex.RandomDuration(result.Config.WatchConfig.StartupDelay.Maximum)

			ll.Info("Using startup delay before initializing config watcher",
				zap.Duration("delay", startupDelay),
			)
		}

		watchFunc, err := watcher.New(watcher.Options{
			Interval: result.Config.WatchConfig.Interval,
			Logger:   ll,
			Paths:    *configPathFlag,
			Callback: func() {
				ll.Info("Configuration changed, triggering reload")

				rs.Reload()
			},
		})
		if err != nil {
			baseLogger.Error("Could not create watcher", zap.Error(err))
			return
		}

		go func() {
			// Sleep for startupDelay to prevent synchronized reloads across
			// different instances of the router
			time.Sleep(startupDelay)

			if err := watchFunc(rootCtx); err != nil {
				if !errors.Is(err, context.Canceled) {
					ll.Error("Error watching execution config", zap.Error(err))
				} else {
					ll.Debug("Watcher context cancelled, shutting down")
				}
			}
		}()

		ll.Info("Watching router config file",
			zap.Strings("config_file", *configPathFlag),
			zap.Duration("watch_interval", result.Config.WatchConfig.Interval),
		)
	} else {
		baseLogger.Info("Config file watching is disabled, you can still trigger reloads by sending SIGHUP to the router process")
	}

	// Start the router supervisor (blocking)
	if err := rs.Start(); err != nil {
		if errors.Is(err, core.ErrStartupFailed) {
			baseLogger.Error("Could not start router", zap.Error(err))
		} else {
			baseLogger.Error("Could not shutdown router gracefully", zap.Error(err))
		}
	}
}