node_exporter/collector/zoneinfo_linux.go

240 lines
9.4 KiB
Go
Raw Permalink Normal View History

// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package collector
import (
"fmt"
"log/slog"
"reflect"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
)
const zoneinfoSubsystem = "zoneinfo"
type zoneinfoCollector struct {
gaugeMetricDescs map[string]*prometheus.Desc
counterMetricDescs map[string]*prometheus.Desc
logger *slog.Logger
fs procfs.FS
}
func init() {
registerCollector("zoneinfo", defaultDisabled, NewZoneinfoCollector)
}
// NewZoneinfoCollector returns a new Collector exposing zone stats.
func NewZoneinfoCollector(logger *slog.Logger) (Collector, error) {
fs, err := procfs.NewFS(*procPath)
if err != nil {
return nil, fmt.Errorf("failed to open procfs: %w", err)
}
return &zoneinfoCollector{
gaugeMetricDescs: createGaugeMetricDescriptions(),
counterMetricDescs: createCounterMetricDescriptions(),
logger: logger,
fs: fs,
}, nil
}
func (c *zoneinfoCollector) Update(ch chan<- prometheus.Metric) error {
metrics, err := c.fs.Zoneinfo()
if err != nil {
return fmt.Errorf("couldn't get zoneinfo: %w", err)
}
for _, metric := range metrics {
node := metric.Node
zone := metric.Zone
metricStruct := reflect.ValueOf(metric)
typeOfMetricStruct := metricStruct.Type()
for i := 0; i < metricStruct.NumField(); i++ {
value := reflect.Indirect(metricStruct.Field(i))
if value.Kind() != reflect.Int64 {
continue
}
metricName := typeOfMetricStruct.Field(i).Name
desc, ok := c.gaugeMetricDescs[metricName]
metricType := prometheus.GaugeValue
if !ok {
desc = c.counterMetricDescs[metricName]
metricType = prometheus.CounterValue
}
ch <- prometheus.MustNewConstMetric(desc, metricType,
float64(reflect.Indirect(metricStruct.Field(i)).Int()),
node, zone)
}
for i, value := range metric.Protection {
metricName := fmt.Sprintf("protection_%d", i)
desc, ok := c.gaugeMetricDescs[metricName]
if !ok {
desc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, metricName),
fmt.Sprintf("Protection array %d. field", i),
[]string{"node", "zone"}, nil)
c.gaugeMetricDescs[metricName] = desc
}
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue,
float64(*value), node, zone)
}
}
return nil
}
func createGaugeMetricDescriptions() map[string]*prometheus.Desc {
return map[string]*prometheus.Desc{
"NrFreePages": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_free_pages"),
"Total number of free pages in the zone",
[]string{"node", "zone"}, nil),
"Min": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "min_pages"),
"Zone watermark pages_min",
[]string{"node", "zone"}, nil),
"Low": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "low_pages"),
"Zone watermark pages_low",
[]string{"node", "zone"}, nil),
"High": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "high_pages"),
"Zone watermark pages_high",
[]string{"node", "zone"}, nil),
"Scanned": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "scanned_pages"),
"Pages scanned since last reclaim",
[]string{"node", "zone"}, nil),
"Spanned": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "spanned_pages"),
"Total pages spanned by the zone, including holes",
[]string{"node", "zone"}, nil),
"Present": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "present_pages"),
"Physical pages existing within the zone",
[]string{"node", "zone"}, nil),
"Managed": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "managed_pages"),
"Present pages managed by the buddy system",
[]string{"node", "zone"}, nil),
"NrActiveAnon": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_active_anon_pages"),
"Number of anonymous pages recently more used",
[]string{"node", "zone"}, nil),
"NrInactiveAnon": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_inactive_anon_pages"),
"Number of anonymous pages recently less used",
[]string{"node", "zone"}, nil),
"NrIsolatedAnon": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_isolated_anon_pages"),
"Temporary isolated pages from anon lru",
[]string{"node", "zone"}, nil),
"NrAnonPages": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_anon_pages"),
"Number of anonymous pages currently used by the system",
[]string{"node", "zone"}, nil),
"NrAnonTransparentHugepages": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_anon_transparent_hugepages"),
"Number of anonymous transparent huge pages currently used by the system",
[]string{"node", "zone"}, nil),
"NrActiveFile": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_active_file_pages"),
"Number of active pages with file-backing",
[]string{"node", "zone"}, nil),
"NrInactiveFile": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_inactive_file_pages"),
"Number of inactive pages with file-backing",
[]string{"node", "zone"}, nil),
"NrIsolatedFile": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_isolated_file_pages"),
"Temporary isolated pages from file lru",
[]string{"node", "zone"}, nil),
"NrFilePages": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_file_pages"),
"Number of file pages",
[]string{"node", "zone"}, nil),
"NrSlabReclaimable": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_slab_reclaimable_pages"),
"Number of reclaimable slab pages",
[]string{"node", "zone"}, nil),
"NrSlabUnreclaimable": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_slab_unreclaimable_pages"),
"Number of unreclaimable slab pages",
[]string{"node", "zone"}, nil),
"NrMlockStack": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_mlock_stack_pages"),
"mlock()ed pages found and moved off LRU",
[]string{"node", "zone"}, nil),
"NrKernelStack": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_kernel_stacks"),
"Number of kernel stacks",
[]string{"node", "zone"}, nil),
"NrMapped": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_mapped_pages"),
"Number of mapped pages",
[]string{"node", "zone"}, nil),
"NrDirty": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_dirty_pages"),
"Number of dirty pages",
[]string{"node", "zone"}, nil),
"NrWriteback": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_writeback_pages"),
"Number of writeback pages",
[]string{"node", "zone"}, nil),
"NrUnevictable": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_unevictable_pages"),
"Number of unevictable pages",
[]string{"node", "zone"}, nil),
"NrShmem": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_shmem_pages"),
"Number of shmem pages (included tmpfs/GEM pages)",
[]string{"node", "zone"}, nil),
}
}
func createCounterMetricDescriptions() map[string]*prometheus.Desc {
return map[string]*prometheus.Desc{
"NrDirtied": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_dirtied_total"),
"Page dirtyings since bootup",
[]string{"node", "zone"}, nil),
"NrWritten": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "nr_written_total"),
"Page writings since bootup",
[]string{"node", "zone"}, nil),
"NumaHit": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_hit_total"),
"Allocated in intended node",
[]string{"node", "zone"}, nil),
"NumaMiss": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_miss_total"),
"Allocated in non intended node",
[]string{"node", "zone"}, nil),
"NumaForeign": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_foreign_total"),
"Was intended here, hit elsewhere",
[]string{"node", "zone"}, nil),
"NumaInterleave": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_interleave_total"),
"Interleaver preferred this zone",
[]string{"node", "zone"}, nil),
"NumaLocal": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_local_total"),
"Allocation from local node",
[]string{"node", "zone"}, nil),
"NumaOther": prometheus.NewDesc(
prometheus.BuildFQName(namespace, zoneinfoSubsystem, "numa_other_total"),
"Allocation from other node",
[]string{"node", "zone"}, nil),
}
}