package observability import ( "context" "database/sql" "errors" "runtime" "time" "git.yuntuo.hk/k2safe/orbit/internal/buildinfo" ) // RuntimeConfig 是节点启动后实际生效的后台控制参数。 // 它只用于固定基数观测,不参与业务授权或持久化状态判断。 type RuntimeConfig struct { WorkflowTickInterval time.Duration ScheduleTickInterval time.Duration RecoveryTickInterval time.Duration BatchLimit int ShutdownTimeout time.Duration } // RecordRuntimeConfig records the effective bounded controller settings. func (r *Registry) RecordRuntimeConfig(config RuntimeConfig) { if r == nil || config.WorkflowTickInterval <= 0 || config.ScheduleTickInterval <= 0 || config.RecoveryTickInterval <= 0 || config.BatchLimit <= 0 || config.ShutdownTimeout <= 0 { return } r.SetGauge("orbit_runtime_tick_interval_seconds", config.WorkflowTickInterval.Seconds(), Labels{"component": "workflow"}) r.SetGauge("orbit_runtime_tick_interval_seconds", config.ScheduleTickInterval.Seconds(), Labels{"component": "schedule"}) r.SetGauge("orbit_runtime_tick_interval_seconds", config.RecoveryTickInterval.Seconds(), Labels{"component": "execution_recovery"}) r.SetGauge("orbit_runtime_batch_limit", float64(config.BatchLimit), nil) r.SetGauge("orbit_shutdown_timeout_seconds", config.ShutdownTimeout.Seconds(), nil) } // RecordDraining records whether this process has entered graceful shutdown. // The value is a fixed, node-local gauge: 0 means serving and 1 means draining. func (r *Registry) RecordDraining(draining bool) { if r == nil { return } value := 0.0 if draining { value = 1 } r.SetGauge("orbit_process_draining", value, nil) } // initializeProcess is called once by the registry constructor. func (r *Registry) initializeProcess() { if r == nil { return } r.SetGauge("orbit_process_start_time_seconds", float64(r.started.Unix()), nil) r.RecordDraining(false) build := buildinfo.Current() r.SetGauge("orbit_build_info", 1, Labels{"go_version": build.GoVersion, "version": build.Version, "revision": build.Revision}) r.RecordProcess() for _, component := range components { labels := Labels{"component": component} r.SetGauge("orbit_runtime_batch_last_success_timestamp_seconds", 0, labels) for _, outcome := range []string{"success", "error", "cancelled"} { r.AddCounter("orbit_runtime_batch_total", 0, Labels{"component": component, "outcome": outcome}) } } for _, route := range []string{"worker_connect", "event_connect"} { r.SetGauge("orbit_websocket_connections", 0, Labels{"route": route}) r.AddCounter("orbit_websocket_connections_total", 0, Labels{"route": route}) } } func (r *Registry) RecordProcess() { if r == nil { return } r.SetGauge("orbit_process_uptime_seconds", time.Since(r.started).Seconds(), nil) r.SetGauge("orbit_go_goroutines", float64(runtime.NumGoroutine()), nil) } func (r *Registry) RecordBatch(component string, started time.Time, err error) { if r == nil { return } outcome := "success" if err != nil { outcome = "error" if errors.Is(err, context.Canceled) { outcome = "cancelled" } } r.AddCounter("orbit_runtime_batch_total", 1, Labels{"component": component, "outcome": outcome}) r.ObserveHistogram("orbit_runtime_batch_duration_seconds", time.Since(started).Seconds(), Labels{"component": component}) if err == nil { r.SetGauge("orbit_runtime_batch_last_success_timestamp_seconds", float64(time.Now().Unix()), Labels{"component": component}) } } // RecordDatabase reads the local connection-pool snapshot; it issues no SQL. func (r *Registry) RecordDatabase(s sql.DBStats) { r.SetGauge("orbit_db_connections_open", float64(s.OpenConnections), nil) r.SetGauge("orbit_db_connections_in_use", float64(s.InUse), nil) r.SetGauge("orbit_db_connections_idle", float64(s.Idle), nil) r.SetGauge("orbit_db_connections_max_open", float64(s.MaxOpenConnections), nil) r.SetCounter("orbit_db_wait_count_total", float64(s.WaitCount), nil) r.SetCounter("orbit_db_wait_duration_seconds_total", s.WaitDuration.Seconds(), nil) }