@@ -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,39 @@ 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
62+ func getPluginSubsystem (pluginName string ) string {
63+ normalizedName := normalizePluginName (pluginName )
64+ return DeschedulerSubsystem + "_" + normalizedName
65+ }
66+
3267var (
3368 PodsEvicted = metrics .NewCounterVec (
3469 & metrics.CounterOpts {
@@ -105,6 +140,139 @@ var (
105140
106141var registerMetrics sync.Once
107142
143+ type PluginMetricsRegistry struct {
144+ pluginMetricsMap map [string ]map [string ]interface {} // plugin -> metric name -> metric object
145+ shouldExport bool
146+ mutex sync.RWMutex
147+ }
148+
149+ func NewPluginMetricsRegistry () * PluginMetricsRegistry {
150+ return & PluginMetricsRegistry {
151+ pluginMetricsMap : make (map [string ]map [string ]interface {}),
152+ shouldExport : true , // Default to true, can be updated later
153+ }
154+ }
155+
156+ func (r * PluginMetricsRegistry ) SetShouldExport (shouldExport bool ) {
157+ r .mutex .Lock ()
158+ defer r .mutex .Unlock ()
159+ r .shouldExport = shouldExport
160+ }
161+
162+ func (r * PluginMetricsRegistry ) ShouldExport () bool {
163+ r .mutex .RLock ()
164+ defer r .mutex .RUnlock ()
165+ return r .shouldExport
166+ }
167+
168+ // RegisterMetricsWithNames registers metrics for a specific plugin with name mapping
169+ func (r * PluginMetricsRegistry ) RegisterNamedPluginMetrics (pluginName string , namedMetrics map [string ]metrics.Registerable ) {
170+ r .mutex .Lock ()
171+ defer r .mutex .Unlock ()
172+
173+ // Initialize the plugin metrics map if needed
174+ if r .pluginMetricsMap [pluginName ] == nil {
175+ r .pluginMetricsMap [pluginName ] = make (map [string ]interface {})
176+ }
177+
178+ for name , metric := range namedMetrics {
179+ // Store in the metrics map
180+ r.pluginMetricsMap [pluginName ][name ] = metric
181+
182+ // Register the metric
183+ legacyregistry .MustRegister (metric )
184+ }
185+ }
186+
187+ func (r * PluginMetricsRegistry ) GetPluginSubsystem (pluginName string ) string {
188+ return getPluginSubsystem (pluginName )
189+ }
190+
191+ func (r * PluginMetricsRegistry ) GetPluginMetric (pluginName , metricName string ) interface {} {
192+ r .mutex .RLock ()
193+ defer r .mutex .RUnlock ()
194+
195+ if pluginMap , exists := r .pluginMetricsMap [pluginName ]; exists {
196+ if metric , exists := pluginMap [metricName ]; exists {
197+ return metric
198+ }
199+ }
200+ return nil
201+ }
202+
203+ func (r * PluginMetricsRegistry ) HandlePluginMetric (pluginName , metricName string ) MetricsHandler {
204+ metric := r .GetPluginMetric (pluginName , metricName )
205+ return newMetricsHandler (metric , r .ShouldExport ())
206+ }
207+
208+ type metricsHandler struct {
209+ metric interface {} // Could be *metrics.GaugeVec, *metrics.CounterVec, etc.
210+ shouldExport bool
211+ currentLabels []string
212+ }
213+
214+ func newMetricsHandler (metric interface {}, shouldExport bool ) * metricsHandler {
215+ return & metricsHandler {
216+ metric : metric ,
217+ shouldExport : shouldExport ,
218+ }
219+ }
220+
221+ func (h * metricsHandler ) WithLabelValues (lvs ... string ) MetricsHandler {
222+ if ! h .shouldExport || h .metric == nil {
223+ return h // Return no-op handler
224+ }
225+
226+ newHandler := & metricsHandler {
227+ metric : h .metric ,
228+ shouldExport : h .shouldExport ,
229+ currentLabels : lvs ,
230+ }
231+ return newHandler
232+ }
233+
234+ func (h * metricsHandler ) Set (val float64 ) {
235+ if ! h .shouldExport || h .metric == nil {
236+ return
237+ }
238+
239+ if gaugeVec , ok := h .metric .(* metrics.GaugeVec ); ok {
240+ gaugeVec .WithLabelValues (h .currentLabels ... ).Set (val )
241+ }
242+ }
243+
244+ func (h * metricsHandler ) Inc () {
245+ if ! h .shouldExport || h .metric == nil {
246+ return
247+ }
248+
249+ if counterVec , ok := h .metric .(* metrics.CounterVec ); ok {
250+ counterVec .WithLabelValues (h .currentLabels ... ).Inc ()
251+ }
252+ }
253+
254+ func (h * metricsHandler ) Add (val float64 ) {
255+ if ! h .shouldExport || h .metric == nil {
256+ return
257+ }
258+
259+ if counterVec , ok := h .metric .(* metrics.CounterVec ); ok {
260+ counterVec .WithLabelValues (h .currentLabels ... ).Add (val )
261+ }
262+ }
263+
264+ func (h * metricsHandler ) Observe (val float64 ) {
265+ if ! h .shouldExport || h .metric == nil {
266+ return
267+ }
268+
269+ if histogramVec , ok := h .metric .(* metrics.HistogramVec ); ok {
270+ histogramVec .WithLabelValues (h .currentLabels ... ).Observe (val )
271+ }
272+ }
273+
274+ var PluginRegistry = NewPluginMetricsRegistry ()
275+
108276// Register all metrics.
109277func Register () {
110278 // Register the metrics.
0 commit comments