in router/core/router.go [769:928]
func (r *Router) bootstrap(ctx context.Context) error {
if !r.bootstrapped.CompareAndSwap(false, true) {
return fmt.Errorf("router is already bootstrapped")
}
cosmoCloudTracingEnabled := r.traceConfig.Enabled && rtrace.DefaultExporter(r.traceConfig) != nil
artInProductionEnabled := r.engineExecutionConfiguration.EnableRequestTracing && !r.developmentMode
needsRegistration := cosmoCloudTracingEnabled || artInProductionEnabled
if needsRegistration && r.selfRegister != nil {
r.logger.Info("Registering router with control plane because you opted in to send telemetry to Cosmo Cloud or advanced request tracing (ART) in production")
ri, registerErr := r.selfRegister.Register(ctx)
if registerErr != nil {
r.logger.Warn("Failed to register router on the control plane. If this warning persists, please contact support.")
} else {
r.registrationInfo = ri
// Only ensure sampling rate if the user exports traces to Cosmo Cloud
if cosmoCloudTracingEnabled {
if r.traceConfig.Sampler > float64(r.registrationInfo.AccountLimits.TraceSamplingRate) {
r.logger.Warn("Trace sampling rate is higher than account limit. Using account limit instead. Please contact support to increase your account limit.",
zap.Float64("limit", r.traceConfig.Sampler),
zap.String("account_limit", fmt.Sprintf("%.2f", r.registrationInfo.AccountLimits.TraceSamplingRate)),
)
r.traceConfig.Sampler = float64(r.registrationInfo.AccountLimits.TraceSamplingRate)
}
}
}
}
if r.traceConfig.Enabled {
tp, err := rtrace.NewTracerProvider(ctx, &rtrace.ProviderConfig{
Logger: r.logger,
Config: r.traceConfig,
ServiceInstanceID: r.instanceID,
IPAnonymization: &rtrace.IPAnonymizationConfig{
Enabled: r.ipAnonymization.Enabled,
Method: rtrace.IPAnonymizationMethod(r.ipAnonymization.Method),
},
MemoryExporter: r.traceConfig.TestMemoryExporter,
})
if err != nil {
return fmt.Errorf("failed to start trace agent: %w", err)
}
r.tracerProvider = tp
}
// Prometheus metrics rely on OTLP metrics
if r.metricConfig.IsEnabled() {
if r.metricConfig.Prometheus.Enabled {
mp, registry, err := rmetric.NewPrometheusMeterProvider(ctx, r.metricConfig, r.instanceID)
if err != nil {
return fmt.Errorf("failed to create Prometheus exporter: %w", err)
}
r.promMeterProvider = mp
r.prometheusServer = rmetric.NewPrometheusServer(r.logger, r.metricConfig.Prometheus.ListenAddr, r.metricConfig.Prometheus.Path, registry)
go func() {
if err := r.prometheusServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
r.logger.Error("Failed to start Prometheus server", zap.Error(err))
}
}()
}
if r.metricConfig.OpenTelemetry.Enabled {
mp, err := rmetric.NewOtlpMeterProvider(ctx, r.logger, r.metricConfig, r.instanceID)
if err != nil {
return fmt.Errorf("failed to start trace agent: %w", err)
}
r.otlpMeterProvider = mp
}
}
if r.graphqlMetricsConfig.Enabled {
client := graphqlmetricsv1connect.NewGraphQLMetricsServiceClient(
http.DefaultClient,
r.graphqlMetricsConfig.CollectorEndpoint,
connect.WithSendGzip(),
)
ge, err := graphqlmetrics.NewExporter(
r.logger,
client,
r.graphApiToken,
graphqlmetrics.NewDefaultExporterSettings(),
)
if err != nil {
return fmt.Errorf("failed to validate graphql metrics exporter: %w", err)
}
r.gqlMetricsExporter = ge
r.logger.Info("GraphQL schema coverage metrics enabled")
}
if r.Config.rateLimit != nil && r.Config.rateLimit.Enabled {
var err error
r.redisClient, err = rd.NewRedisCloser(&rd.RedisCloserOptions{
URLs: r.Config.rateLimit.Storage.URLs,
ClusterEnabled: r.Config.rateLimit.Storage.ClusterEnabled,
Logger: r.logger,
})
if err != nil {
return fmt.Errorf("failed to create redis client: %w", err)
}
}
if r.metricConfig.OpenTelemetry.EngineStats.Enabled() || r.metricConfig.Prometheus.EngineStats.Enabled() || r.engineExecutionConfiguration.Debug.ReportWebSocketConnections {
r.EngineStats = statistics.NewEngineStats(ctx, r.logger, r.engineExecutionConfiguration.Debug.ReportWebSocketConnections)
}
if r.engineExecutionConfiguration.Debug.ReportMemoryUsage {
debug.ReportMemoryUsage(ctx, r.logger)
}
if r.playgroundConfig.Enabled {
playgroundUrl, err := url.JoinPath(r.baseURL, r.playgroundConfig.Path)
if err != nil {
return fmt.Errorf("failed to join playground url: %w", err)
}
r.logger.Info("Serving GraphQL playground", zap.String("url", playgroundUrl))
r.playgroundHandler = graphiql.NewPlayground(&graphiql.PlaygroundOptions{
Html: graphiql.PlaygroundHTML(),
GraphqlURL: r.graphqlWebURL,
PlaygroundPath: r.playgroundPath,
ConcurrencyLimit: int64(r.playgroundConfig.ConcurrencyLimit),
})
}
if r.executionConfig != nil && r.executionConfig.Path != "" {
executionConfig, err := execution_config.FromFile(r.executionConfig.Path)
if err != nil {
return fmt.Errorf("failed to read execution config: %w", err)
}
r.staticExecutionConfig = executionConfig
}
if err := r.buildClients(); err != nil {
return err
}
// Modules are only initialized once and not on every config change
if err := r.initModules(ctx); err != nil {
return fmt.Errorf("failed to init user modules: %w", err)
}
if r.traceConfig.Enabled && len(r.tracePropagators) > 0 {
r.compositePropagator = propagation.NewCompositeTextMapPropagator(r.tracePropagators...)
// Don't set it globally when we use the router in tests.
// In practice, setting it globally only makes sense for module development.
if r.traceConfig.TestMemoryExporter == nil {
otel.SetTextMapPropagator(r.compositePropagator)
}
}
return nil
}