@@ -17,7 +17,9 @@ limitations under the License.
1717package metrics
1818
1919import (
20+ "strings"
2021 "sync"
22+ "unicode"
2123
2224 "k8s.io/component-base/metrics"
2325 "k8s.io/component-base/metrics/legacyregistry"
@@ -29,6 +31,43 @@ const (
2931 DeschedulerSubsystem = "descheduler"
3032)
3133
34+ // MetricsHandler provides a smart wrapper for metrics that handles export logic
35+ type MetricsHandler interface {
36+ // WithLabelValues is equivalent to metrics.WithLabelValues but respects ShouldExportMetrics
37+ WithLabelValues (lvs ... string ) MetricsHandler
38+ // Set sets the gauge value (for GaugeVec metrics)
39+ Set (val float64 )
40+ // Inc increments the counter (for CounterVec metrics)
41+ Inc ()
42+ // Add adds a value to the counter (for CounterVec metrics)
43+ Add (val float64 )
44+ // Observe observes a value for histogram (for HistogramVec metrics)
45+ Observe (val float64 )
46+ }
47+
48+ // normalizePluginName converts a plugin name to a normalized form for use in metrics subsystems
49+ // Examples: "LowNodeUtilization" -> "low_node_utilization", "RemovePodsViolatingNodeTaints" -> "remove_pods_violating_node_taints"
50+ func normalizePluginName (pluginName string ) string {
51+ var result strings.Builder
52+ for i , r := range pluginName {
53+ if unicode .IsUpper (r ) && i > 0 {
54+ result .WriteRune ('_' )
55+ }
56+ result .WriteRune (unicode .ToLower (r ))
57+ }
58+ return result .String ()
59+ }
60+
61+ // getPluginSubsystem returns the subsystem name for a plugin, optionally including profile name
62+ func getPluginSubsystem (profileName , pluginName string ) string {
63+ normalizedPluginName := normalizePluginName (pluginName )
64+ if profileName == "" {
65+ return DeschedulerSubsystem + "_" + normalizedPluginName
66+ }
67+ normalizedProfileName := normalizePluginName (profileName )
68+ return DeschedulerSubsystem + "_" + normalizedProfileName + "_" + normalizedPluginName
69+ }
70+
3271var (
3372 PodsEvicted = metrics .NewCounterVec (
3473 & metrics.CounterOpts {
@@ -105,6 +144,139 @@ var (
105144
106145var registerMetrics sync.Once
107146
147+ type PluginMetricsRegistry struct {
148+ pluginMetricsMap map [string ]map [string ]interface {} // plugin -> metric name -> metric object
149+ shouldExport bool
150+ mutex sync.RWMutex
151+ }
152+
153+ func NewPluginMetricsRegistry () * PluginMetricsRegistry {
154+ return & PluginMetricsRegistry {
155+ pluginMetricsMap : make (map [string ]map [string ]interface {}),
156+ shouldExport : true , // Default to true, can be updated later
157+ }
158+ }
159+
160+ func (r * PluginMetricsRegistry ) SetShouldExport (shouldExport bool ) {
161+ r .mutex .Lock ()
162+ defer r .mutex .Unlock ()
163+ r .shouldExport = shouldExport
164+ }
165+
166+ func (r * PluginMetricsRegistry ) ShouldExport () bool {
167+ r .mutex .RLock ()
168+ defer r .mutex .RUnlock ()
169+ return r .shouldExport
170+ }
171+
172+ // RegisterMetricsWithNames registers metrics for a specific plugin with name mapping
173+ func (r * PluginMetricsRegistry ) RegisterNamedPluginMetrics (pluginName string , namedMetrics map [string ]metrics.Registerable ) {
174+ r .mutex .Lock ()
175+ defer r .mutex .Unlock ()
176+
177+ // Initialize the plugin metrics map if needed
178+ if r .pluginMetricsMap [pluginName ] == nil {
179+ r .pluginMetricsMap [pluginName ] = make (map [string ]interface {})
180+ }
181+
182+ for name , metric := range namedMetrics {
183+ // Store in the metrics map
184+ r.pluginMetricsMap [pluginName ][name ] = metric
185+
186+ // Register the metric
187+ legacyregistry .MustRegister (metric )
188+ }
189+ }
190+
191+ func (r * PluginMetricsRegistry ) GetPluginSubsystem (profileName , pluginName string ) string {
192+ return getPluginSubsystem (profileName , pluginName )
193+ }
194+
195+ func (r * PluginMetricsRegistry ) GetPluginMetric (pluginName , metricName string ) interface {} {
196+ r .mutex .RLock ()
197+ defer r .mutex .RUnlock ()
198+
199+ if pluginMap , exists := r .pluginMetricsMap [pluginName ]; exists {
200+ if metric , exists := pluginMap [metricName ]; exists {
201+ return metric
202+ }
203+ }
204+ return nil
205+ }
206+
207+ func (r * PluginMetricsRegistry ) HandlePluginMetric (pluginName , metricName string ) MetricsHandler {
208+ metric := r .GetPluginMetric (pluginName , metricName )
209+ return newMetricsHandler (metric , r .ShouldExport ())
210+ }
211+
212+ type metricsHandler struct {
213+ metric interface {} // Could be *metrics.GaugeVec, *metrics.CounterVec, etc.
214+ shouldExport bool
215+ currentLabels []string
216+ }
217+
218+ func newMetricsHandler (metric interface {}, shouldExport bool ) * metricsHandler {
219+ return & metricsHandler {
220+ metric : metric ,
221+ shouldExport : shouldExport ,
222+ }
223+ }
224+
225+ func (h * metricsHandler ) WithLabelValues (lvs ... string ) MetricsHandler {
226+ if ! h .shouldExport || h .metric == nil {
227+ return h // Return no-op handler
228+ }
229+
230+ newHandler := & metricsHandler {
231+ metric : h .metric ,
232+ shouldExport : h .shouldExport ,
233+ currentLabels : lvs ,
234+ }
235+ return newHandler
236+ }
237+
238+ func (h * metricsHandler ) Set (val float64 ) {
239+ if ! h .shouldExport || h .metric == nil {
240+ return
241+ }
242+
243+ if gaugeVec , ok := h .metric .(* metrics.GaugeVec ); ok {
244+ gaugeVec .WithLabelValues (h .currentLabels ... ).Set (val )
245+ }
246+ }
247+
248+ func (h * metricsHandler ) Inc () {
249+ if ! h .shouldExport || h .metric == nil {
250+ return
251+ }
252+
253+ if counterVec , ok := h .metric .(* metrics.CounterVec ); ok {
254+ counterVec .WithLabelValues (h .currentLabels ... ).Inc ()
255+ }
256+ }
257+
258+ func (h * metricsHandler ) Add (val float64 ) {
259+ if ! h .shouldExport || h .metric == nil {
260+ return
261+ }
262+
263+ if counterVec , ok := h .metric .(* metrics.CounterVec ); ok {
264+ counterVec .WithLabelValues (h .currentLabels ... ).Add (val )
265+ }
266+ }
267+
268+ func (h * metricsHandler ) Observe (val float64 ) {
269+ if ! h .shouldExport || h .metric == nil {
270+ return
271+ }
272+
273+ if histogramVec , ok := h .metric .(* metrics.HistogramVec ); ok {
274+ histogramVec .WithLabelValues (h .currentLabels ... ).Observe (val )
275+ }
276+ }
277+
278+ var PluginRegistry = NewPluginMetricsRegistry ()
279+
108280// Register all metrics.
109281func Register () {
110282 // Register the metrics.
0 commit comments