diff --git a/collector/btrfs_linux.go b/collector/btrfs_linux.go index 2dbdf5aa..acb7e64c 100644 --- a/collector/btrfs_linux.go +++ b/collector/btrfs_linux.go @@ -69,7 +69,7 @@ func (c *btrfsCollector) Update(ch chan<- prometheus.Metric) error { for _, s := range stats { // match up procfs and ioctl info by filesystem UUID (without dashes) - var fsUUID = strings.Replace(s.UUID, "-", "", -1) + var fsUUID = strings.ReplaceAll(s.UUID, "-", "") ioctlStats := ioctlStatsMap[fsUUID] c.updateBtrfsStats(ch, s, ioctlStats) } diff --git a/collector/cpu_aix.go b/collector/cpu_aix.go index 9d896e2d..017e1a0c 100644 --- a/collector/cpu_aix.go +++ b/collector/cpu_aix.go @@ -30,22 +30,50 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +var ( + nodeCPUPhysicalSecondsDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "physical_seconds_total"), + "Seconds the physical CPUs spent in each mode.", + []string{"cpu", "mode"}, nil, + ) + nodeCPUSRunQueueDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "runqueue"), + "Length of the run queue.", []string{"cpu"}, nil, + ) + nodeCPUFlagsDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "flags"), + "CPU flags.", + []string{"cpu", "flag"}, nil, + ) + nodeCPUContextSwitchDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "context_switches_total"), + "Number of context switches.", + []string{"cpu"}, nil, + ) +) + type cpuCollector struct { - cpu typedDesc - logger *slog.Logger - tickPerSecond int64 + cpu typedDesc + cpuPhysical typedDesc + cpuRunQueue typedDesc + cpuFlags typedDesc + cpuContextSwitch typedDesc + + logger *slog.Logger + tickPerSecond float64 + purrTicksPerSecond float64 } func init() { registerCollector("cpu", defaultEnabled, NewCpuCollector) } -func tickPerSecond() (int64, error) { +func tickPerSecond() (float64, error) { ticks, err := C.sysconf(C._SC_CLK_TCK) if ticks == -1 || err != nil { return 0, fmt.Errorf("failed to get clock ticks per second: %v", err) } - return int64(ticks), nil + return float64(ticks), nil } func NewCpuCollector(logger *slog.Logger) (Collector, error) { @@ -53,10 +81,22 @@ func NewCpuCollector(logger *slog.Logger) (Collector, error) { if err != nil { return nil, err } + + pconfig, err := perfstat.PartitionStat() + + if err != nil { + return nil, err + } + return &cpuCollector{ - cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, - logger: logger, - tickPerSecond: ticks, + cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + cpuPhysical: typedDesc{nodeCPUPhysicalSecondsDesc, prometheus.CounterValue}, + cpuRunQueue: typedDesc{nodeCPUSRunQueueDesc, prometheus.GaugeValue}, + cpuFlags: typedDesc{nodeCPUFlagsDesc, prometheus.GaugeValue}, + cpuContextSwitch: typedDesc{nodeCPUContextSwitchDesc, prometheus.CounterValue}, + logger: logger, + tickPerSecond: ticks, + purrTicksPerSecond: float64(pconfig.ProcessorMhz * 1e6), }, nil } @@ -67,10 +107,26 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { } for n, stat := range stats { - ch <- c.cpu.mustNewConstMetric(float64(stat.User/c.tickPerSecond), strconv.Itoa(n), "user") - ch <- c.cpu.mustNewConstMetric(float64(stat.Sys/c.tickPerSecond), strconv.Itoa(n), "system") - ch <- c.cpu.mustNewConstMetric(float64(stat.Idle/c.tickPerSecond), strconv.Itoa(n), "idle") - ch <- c.cpu.mustNewConstMetric(float64(stat.Wait/c.tickPerSecond), strconv.Itoa(n), "wait") + // LPAR metrics + ch <- c.cpu.mustNewConstMetric(float64(stat.User)/c.tickPerSecond, strconv.Itoa(n), "user") + ch <- c.cpu.mustNewConstMetric(float64(stat.Sys)/c.tickPerSecond, strconv.Itoa(n), "system") + ch <- c.cpu.mustNewConstMetric(float64(stat.Idle)/c.tickPerSecond, strconv.Itoa(n), "idle") + ch <- c.cpu.mustNewConstMetric(float64(stat.Wait)/c.tickPerSecond, strconv.Itoa(n), "wait") + + // Physical CPU metrics + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PIdle)/c.purrTicksPerSecond, strconv.Itoa(n), "pidle") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PUser)/c.purrTicksPerSecond, strconv.Itoa(n), "puser") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PSys)/c.purrTicksPerSecond, strconv.Itoa(n), "psys") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PWait)/c.purrTicksPerSecond, strconv.Itoa(n), "pwait") + + // Run queue length + ch <- c.cpuRunQueue.mustNewConstMetric(float64(stat.RunQueue), strconv.Itoa(n)) + + // Flags + ch <- c.cpuFlags.mustNewConstMetric(float64(stat.SpurrFlag), strconv.Itoa(n), "spurr") + + // Context switches + ch <- c.cpuContextSwitch.mustNewConstMetric(float64(stat.CSwitches), strconv.Itoa(n)) } return nil } diff --git a/collector/cpu_linux.go b/collector/cpu_linux.go index 8ca70365..078a4611 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -87,7 +87,7 @@ func NewCPUCollector(logger *slog.Logger) (Collector, error) { isolcpus, err := sfs.IsolatedCPUs() if err != nil { if !os.IsNotExist(err) { - return nil, fmt.Errorf("Unable to get isolated cpus: %w", err) + return nil, fmt.Errorf("unable to get isolated cpus: %w", err) } logger.Debug("Could not open isolated file", "error", err) } diff --git a/collector/cpu_netbsd.go b/collector/cpu_netbsd.go index ae1235eb..459365d6 100644 --- a/collector/cpu_netbsd.go +++ b/collector/cpu_netbsd.go @@ -155,7 +155,7 @@ func getCPUTemperatures() (map[int]float64, error) { } keys := sortFilterSysmonProperties(props, "coretemp") - for idx, _ := range keys { + for idx := range keys { convertTemperatures(props[keys[idx]], res) } diff --git a/collector/cpu_vulnerabilities_linux.go b/collector/cpu_vulnerabilities_linux.go index 180d56d6..a41d5b17 100644 --- a/collector/cpu_vulnerabilities_linux.go +++ b/collector/cpu_vulnerabilities_linux.go @@ -22,12 +22,12 @@ import ( ) const ( - cpuVulerabilitiesCollector = "cpu_vulnerabilities" + cpuVulnerabilitiesCollectorSubsystem = "cpu_vulnerabilities" ) var ( vulnerabilityDesc = prometheus.NewDesc( - prometheus.BuildFQName(namespace, cpuVulerabilitiesCollector, "info"), + prometheus.BuildFQName(namespace, cpuVulnerabilitiesCollectorSubsystem, "info"), "Details of each CPU vulnerability reported by sysfs. The value of the series is an int encoded state of the vulnerability. The same state is stored as a string in the label", []string{"codename", "state", "mitigation"}, nil, @@ -37,7 +37,7 @@ var ( type cpuVulnerabilitiesCollector struct{} func init() { - registerCollector(cpuVulerabilitiesCollector, defaultDisabled, NewVulnerabilitySysfsCollector) + registerCollector(cpuVulnerabilitiesCollectorSubsystem, defaultDisabled, NewVulnerabilitySysfsCollector) } func NewVulnerabilitySysfsCollector(logger *slog.Logger) (Collector, error) { diff --git a/collector/diskstats_aix.go b/collector/diskstats_aix.go index c6619fd9..4ad39ff3 100644 --- a/collector/diskstats_aix.go +++ b/collector/diskstats_aix.go @@ -30,11 +30,19 @@ type diskstatsCollector struct { rbytes typedDesc wbytes typedDesc time typedDesc + bsize typedDesc + qdepth typedDesc + + rserv typedDesc + wserv typedDesc + + xfers typedDesc + xrate typedDesc deviceFilter deviceFilter logger *slog.Logger - tickPerSecond int64 + tickPerSecond float64 } func init() { @@ -57,6 +65,54 @@ func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { wbytes: typedDesc{writtenBytesDesc, prometheus.CounterValue}, time: typedDesc{ioTimeSecondsDesc, prometheus.CounterValue}, + bsize: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "block_size_bytes"), + "Size of the block device in bytes.", + diskLabelNames, nil, + ), + prometheus.GaugeValue, + }, + qdepth: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "queue_depth"), + "Number of requests in the queue.", + diskLabelNames, nil, + ), + prometheus.GaugeValue, + }, + rserv: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "read_time_seconds_total"), + "The total time spent servicing read requests.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + wserv: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "write_time_seconds_total"), + "The total time spent servicing write requests.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + xfers: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "transfers_total"), + "The total number of transfers to/from disk.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + xrate: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "transfers_to_disk_total"), + "The total number of transfers from disk.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, deviceFilter: deviceFilter, logger: logger, @@ -76,7 +132,14 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { } ch <- c.rbytes.mustNewConstMetric(float64(stat.Rblks*512), stat.Name) ch <- c.wbytes.mustNewConstMetric(float64(stat.Wblks*512), stat.Name) - ch <- c.time.mustNewConstMetric(float64(stat.Time/c.tickPerSecond), stat.Name) + ch <- c.time.mustNewConstMetric(float64(stat.Time)/float64(c.tickPerSecond), stat.Name) + + ch <- c.bsize.mustNewConstMetric(float64(stat.BSize), stat.Name) + ch <- c.qdepth.mustNewConstMetric(float64(stat.QDepth), stat.Name) + ch <- c.rserv.mustNewConstMetric(float64(stat.Rserv)/1e9, stat.Name) + ch <- c.wserv.mustNewConstMetric(float64(stat.Wserv)/1e9, stat.Name) + ch <- c.xfers.mustNewConstMetric(float64(stat.Xfers), stat.Name) + ch <- c.xrate.mustNewConstMetric(float64(stat.XRate), stat.Name) } return nil } diff --git a/collector/ethtool_linux.go b/collector/ethtool_linux.go index 01410d64..d9f66469 100644 --- a/collector/ethtool_linux.go +++ b/collector/ethtool_linux.go @@ -453,6 +453,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { // Sanitizing the metric names can lead to duplicate metric names. Therefore check for clashes beforehand. metricFQNames := make(map[string]string) + renamedStats := make(map[string]uint64, len(stats)) for metric := range stats { metricName := SanitizeMetricName(metric) if !c.metricsPattern.MatchString(metricName) { @@ -467,6 +468,8 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { metricFQNames[metricFQName] = "" } else { metricFQNames[metricFQName] = metricName + // Later we'll go look for the stat with the "sanitized" metric name, so we can copy it there already + renamedStats[metricName] = stats[metric] } } @@ -484,7 +487,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { continue } - val := stats[metric] + val := renamedStats[metric] // Check to see if this metric exists; if not then create it and store it in c.entries. entry := c.entryWithCreate(metric, metricFQName) diff --git a/collector/ethtool_linux_test.go b/collector/ethtool_linux_test.go index 98e66dbc..c72adcb4 100644 --- a/collector/ethtool_linux_test.go +++ b/collector/ethtool_linux_test.go @@ -212,16 +212,18 @@ func (e *EthtoolFixture) LinkInfo(intf string) (ethtool.EthtoolCmd, error) { items := strings.Split(line, ": ") if items[0] == "Supported pause frame use" { - if items[1] == "Symmetric" { + switch items[1] { + case "Symmetric": res.Supported |= (1 << unix.ETHTOOL_LINK_MODE_Pause_BIT) - } else if items[1] == "Receive-only" { + case "Receive-only": res.Supported |= (1 << unix.ETHTOOL_LINK_MODE_Asym_Pause_BIT) } } if items[0] == "Advertised pause frame use" { - if items[1] == "Symmetric" { + switch items[1] { + case "Symmetric": res.Advertising |= (1 << unix.ETHTOOL_LINK_MODE_Pause_BIT) - } else if items[1] == "Receive-only" { + case "Receive-only": res.Advertising |= (1 << unix.ETHTOOL_LINK_MODE_Asym_Pause_BIT) } } @@ -269,6 +271,7 @@ func NewEthtoolTestCollector(logger *slog.Logger) (Collector, error) { func TestBuildEthtoolFQName(t *testing.T) { testcases := map[string]string{ + "port.rx_errors": "node_ethtool_port_received_errors", "rx_errors": "node_ethtool_received_errors", "Queue[0] AllocFails": "node_ethtool_queue_0_allocfails", "Tx LPI entry count": "node_ethtool_transmitted_lpi_entry_count", @@ -292,6 +295,9 @@ node_ethtool_align_errors{device="eth0"} 0 # HELP node_ethtool_info A metric with a constant '1' value labeled by bus_info, device, driver, expansion_rom_version, firmware_version, version. # TYPE node_ethtool_info gauge node_ethtool_info{bus_info="0000:00:1f.6",device="eth0",driver="e1000e",expansion_rom_version="",firmware_version="0.5-4",version="5.11.0-22-generic"} 1 +# HELP node_ethtool_port_received_dropped Network interface port_rx_dropped +# TYPE node_ethtool_port_received_dropped untyped +node_ethtool_port_received_dropped{device="eth0"} 12028 # HELP node_ethtool_received_broadcast Network interface rx_broadcast # TYPE node_ethtool_received_broadcast untyped node_ethtool_received_broadcast{device="eth0"} 5792 diff --git a/collector/filesystem_aix.go b/collector/filesystem_aix.go index a1314a0f..84bb8913 100644 --- a/collector/filesystem_aix.go +++ b/collector/filesystem_aix.go @@ -53,9 +53,9 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { mountPoint: stat.MountPoint, fsType: fstype, }, - size: float64(stat.TotalBlocks / 512.0), - free: float64(stat.FreeBlocks / 512.0), - avail: float64(stat.FreeBlocks / 512.0), // AIX doesn't distinguish between free and available blocks. + size: float64(stat.TotalBlocks * 512.0), + free: float64(stat.FreeBlocks * 512.0), + avail: float64(stat.FreeBlocks * 512.0), // AIX doesn't distinguish between free and available blocks. files: float64(stat.TotalInodes), filesFree: float64(stat.FreeInodes), ro: ro, diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index 3a7bda4d..127a3be1 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -215,8 +215,8 @@ func parseFilesystemLabels(r io.Reader) ([]filesystemLabels, error) { // Ensure we handle the translation of \040 and \011 // as per fstab(5). - parts[4] = strings.Replace(parts[4], "\\040", " ", -1) - parts[4] = strings.Replace(parts[4], "\\011", "\t", -1) + parts[4] = strings.ReplaceAll(parts[4], "\\040", " ") + parts[4] = strings.ReplaceAll(parts[4], "\\011", "\t") filesystems = append(filesystems, filesystemLabels{ device: parts[m+3], diff --git a/collector/filesystem_macos.go b/collector/filesystem_macos.go index 26b30644..cc5c97ce 100644 --- a/collector/filesystem_macos.go +++ b/collector/filesystem_macos.go @@ -21,21 +21,25 @@ package collector #cgo LDFLAGS: -framework Foundation #import Float64 purgeable(char *path) { - CFNumberRef tmp; - NSError *error = nil; - NSString *str = [NSString stringWithUTF8String:path]; - NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:str]; - NSDictionary *results = [fileURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error]; - if (results) { - if ((tmp = CFDictionaryGetValue((CFDictionaryRef)results, NSURLVolumeAvailableCapacityForImportantUsageKey)) == NULL) { - return -1.0f; - } - Float64 value; - if (CFNumberGetValue(tmp, kCFNumberFloat64Type, &value)) { - return value; + Float64 value = -1.0f; + + @autoreleasepool { + NSError *error = nil; + NSString *str = [NSString stringWithUTF8String:path]; + NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:str]; + + NSDictionary *results = [fileURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error]; + if (results) { + CFNumberRef tmp = CFDictionaryGetValue((CFDictionaryRef)results, NSURLVolumeAvailableCapacityForImportantUsageKey); + if (tmp != NULL) { + CFNumberGetValue(tmp, kCFNumberFloat64Type, &value); + } } + + [fileURL release]; } - return -1.0f; + + return value; } */ import "C" @@ -88,6 +92,9 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { ro = 1 } + mountpointCString := C.CString(mountpoint) + defer C.free(unsafe.Pointer(mountpointCString)) + stats = append(stats, filesystemStats{ labels: filesystemLabels{ device: device, @@ -99,7 +106,7 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { avail: float64(mnt[i].f_bavail) * float64(mnt[i].f_bsize), files: float64(mnt[i].f_files), filesFree: float64(mnt[i].f_ffree), - purgeable: float64(C.purgeable(C.CString(mountpoint))), + purgeable: float64(C.purgeable(mountpointCString)), ro: ro, }) } diff --git a/collector/fixtures/ethtool/eth0/statistics b/collector/fixtures/ethtool/eth0/statistics index 80423bd6..81e511e7 100644 --- a/collector/fixtures/ethtool/eth0/statistics +++ b/collector/fixtures/ethtool/eth0/statistics @@ -4,6 +4,7 @@ NIC statistics: rx_packets: 1260062 tx_errors: 0 rx_errors: 0 + port.rx_dropped: 12028 rx_missed: 401 align_errors: 0 tx_single_collisions: 0 diff --git a/collector/meminfo_aix.go b/collector/meminfo_aix.go index ff59105b..f90b1fd8 100644 --- a/collector/meminfo_aix.go +++ b/collector/meminfo_aix.go @@ -40,8 +40,12 @@ func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { } return map[string]float64{ - "total_bytes": float64(stats.RealTotal * 4096), - "free_bytes": float64(stats.RealFree * 4096), - "available_bytes": float64(stats.RealAvailable * 4096), + "total_bytes": float64(stats.RealTotal * 4096), + "free_bytes": float64(stats.RealFree * 4096), + "available_bytes": float64(stats.RealAvailable * 4096), + "process_bytes": float64(stats.RealProcess * 4096), + "paging_space_total_bytes": float64(stats.PgSpTotal * 4096), + "paging_space_free_bytes": float64(stats.PgSpFree * 4096), + "page_scans_total": float64(stats.Scans), }, nil } diff --git a/collector/meminfo_linux.go b/collector/meminfo_linux.go index 98d5a5c0..4e67ac58 100644 --- a/collector/meminfo_linux.go +++ b/collector/meminfo_linux.go @@ -44,7 +44,7 @@ func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { meminfo, err := c.fs.Meminfo() if err != nil { - return nil, fmt.Errorf("Failed to get memory info: %s", err) + return nil, fmt.Errorf("failed to get memory info: %w", err) } metrics := make(map[string]float64) diff --git a/collector/netdev_aix.go b/collector/netdev_aix.go index ae316443..e29371b1 100644 --- a/collector/netdev_aix.go +++ b/collector/netdev_aix.go @@ -32,16 +32,20 @@ func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, err for _, stat := range stats { netDev[stat.Name] = map[string]uint64{ - "receive_packets": uint64(stat.RxPackets), - "transmit_packets": uint64(stat.TxPackets), - "receive_bytes": uint64(stat.RxBytes), - "transmit_bytes": uint64(stat.TxBytes), - "receive_errors": uint64(stat.RxErrors), - "transmit_errors": uint64(stat.TxErrors), - "receive_dropped": uint64(stat.RxPacketsDropped), - "transmit_dropped": uint64(stat.TxPacketsDropped), - "receive_multicast": uint64(stat.RxMulticastPackets), - "transmit_multicast": uint64(stat.TxMulticastPackets), + "receive_bytes": uint64(stat.RxBytes), + "receive_dropped": uint64(stat.RxPacketsDropped), + "receive_errors": uint64(stat.RxErrors), + "receive_multicast": uint64(stat.RxMulticastPackets), + "receive_packets": uint64(stat.RxPackets), + "receive_collision_errors": uint64(stat.RxCollisionErrors), + "transmit_bytes": uint64(stat.TxBytes), + "transmit_dropped": uint64(stat.TxPacketsDropped), + "transmit_errors": uint64(stat.TxErrors), + "transmit_multicast": uint64(stat.TxMulticastPackets), + "transmit_packets": uint64(stat.TxPackets), + "transmit_queue_overflow": uint64(stat.TxQueueOverflow), + "transmit_collision_single_errors": uint64(stat.TxSingleCollisionCount), + "transmit_collision_multiple_errors": uint64(stat.TxMultipleCollisionCount), } } diff --git a/collector/netdev_darwin.go b/collector/netdev_darwin.go index 2367ebe5..42835594 100644 --- a/collector/netdev_darwin.go +++ b/collector/netdev_darwin.go @@ -22,6 +22,7 @@ import ( "fmt" "log/slog" "net" + "unsafe" "golang.org/x/sys/unix" ) @@ -71,51 +72,107 @@ func getIfaceData(index int) (*ifMsghdr2, error) { return nil, err } err = binary.Read(bytes.NewReader(rawData), binary.LittleEndian, &data) + if err != nil { + return &data, err + } + + /* + As of macOS Ventura 13.2.1, there’s a kernel bug which truncates traffic values at the 4GiB mark. + This is a workaround to fetch the interface traffic metrics using a sysctl call. + Apple wants to prevent fingerprinting by 3rdparty apps and might fix this bug in future which would break this implementation. + */ + mib := []int32{ + unix.CTL_NET, + unix.AF_LINK, + 0, // NETLINK_GENERIC: functions not specific to a type of iface + 2, //IFMIB_IFDATA: per-interface data table + int32(index), + 1, // IFDATA_GENERAL: generic stats for all kinds of ifaces + } + + var mibData ifMibData + size := unsafe.Sizeof(mibData) + + if _, _, errno := unix.Syscall6( + unix.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(len(mib)), + uintptr(unsafe.Pointer(&mibData)), + uintptr(unsafe.Pointer(&size)), + uintptr(unsafe.Pointer(nil)), + 0, + ); errno != 0 { + return &data, err + } + + var ifdata ifData64 + err = binary.Read(bytes.NewReader(mibData.Data[:]), binary.LittleEndian, &ifdata) + if err != nil { + return &data, err + } + + data.Data.Ibytes = ifdata.Ibytes + data.Data.Obytes = ifdata.Obytes return &data, err } +// https://github.com/apple-oss-distributions/xnu/blob/main/bsd/net/if.h#L220-L232 type ifMsghdr2 struct { - Msglen uint16 - Version uint8 - Type uint8 - Addrs int32 - Flags int32 - Index uint16 - _ [2]byte - SndLen int32 - SndMaxlen int32 - SndDrops int32 - Timer int32 - Data ifData64 + Msglen uint16 // to skip over non-understood messages + Version uint8 // future binary compatabilit + Type uint8 // message type + Addrs int32 // like rtm_addrs + Flags int32 // value of if_flags + Index uint16 // index for associated ifp + _ [2]byte // padding for alignment + SndLen int32 // instantaneous length of send queue + SndMaxlen int32 // maximum length of send queue + SndDrops int32 // number of drops in send queue + Timer int32 // time until if_watchdog called + Data ifData64 // statistics and other data } -// https://github.com/apple/darwin-xnu/blob/main/bsd/net/if_var.h#L199-L231 +// https://github.com/apple-oss-distributions/xnu/blob/main/bsd/net/if_var.h#L207-L235 type ifData64 struct { - Type uint8 - Typelen uint8 - Physical uint8 - Addrlen uint8 - Hdrlen uint8 - Recvquota uint8 - Xmitquota uint8 - Unused1 uint8 - Mtu uint32 - Metric uint32 - Baudrate uint64 - Ipackets uint64 - Ierrors uint64 - Opackets uint64 - Oerrors uint64 - Collisions uint64 - Ibytes uint64 - Obytes uint64 - Imcasts uint64 - Omcasts uint64 - Iqdrops uint64 - Noproto uint64 - Recvtiming uint32 - Xmittiming uint32 - Lastchange unix.Timeval32 + Type uint8 // ethernet, tokenring, etc + Typelen uint8 // Length of frame type id + Physical uint8 // e.g., AUI, Thinnet, 10base-T, etc + Addrlen uint8 // media address length + Hdrlen uint8 // media header length + Recvquota uint8 // polling quota for receive intrs + Xmitquota uint8 // polling quota for xmit intrs + Unused1 uint8 // for future use + Mtu uint32 // maximum transmission unit + Metric uint32 // routing metric (external only) + Baudrate uint64 // linespeed + + // volatile statistics + Ipackets uint64 // packets received on interface + Ierrors uint64 // input errors on interface + Opackets uint64 // packets sent on interface + Oerrors uint64 // output errors on interface + Collisions uint64 // collisions on csma interfaces + Ibytes uint64 // total number of octets received + Obytes uint64 // total number of octets sent + Imcasts uint64 // packets received via multicast + Omcasts uint64 // packets sent via multicast + Iqdrops uint64 // dropped on input, this interface + Noproto uint64 // destined for unsupported protocol + Recvtiming uint32 // usec spent receiving when timing + Xmittiming uint32 // usec spent xmitting when timing + Lastchange unix.Timeval32 // time of last administrative change +} + +// https://github.com/apple-oss-distributions/xnu/blob/main/bsd/net/if_mib.h#L65-L74 +type ifMibData struct { + Name [16]byte // name of interface + PCount uint32 // number of promiscuous listeners + Flags uint32 // interface flags + SendLength uint32 // instantaneous length of send queue + MaxSendLength uint32 // maximum length of send queue + SendDrops uint32 // number of drops in send queue + _ [4]uint32 // for future expansion + Data [128]byte // generic information and statistics } func getNetDevLabels() (map[string]map[string]string, error) { diff --git a/collector/netinterface_aix.go b/collector/netinterface_aix.go new file mode 100644 index 00000000..9a5b7668 --- /dev/null +++ b/collector/netinterface_aix.go @@ -0,0 +1,86 @@ +// Copyright 2025 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. + +//go:build !nonetinterface +// +build !nonetinterface + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +type netinterfaceCollector struct { + logger *slog.Logger + collisions *prometheus.Desc + ibytes *prometheus.Desc + ipackets *prometheus.Desc + obytes *prometheus.Desc + opackets *prometheus.Desc +} + +const ( + netinterfaceSubsystem = "netinterface" +) + +func init() { + registerCollector("netinterface", defaultEnabled, NewNetinterfaceCollector) +} + +func NewNetinterfaceCollector(logger *slog.Logger) (Collector, error) { + labels := []string{"interface"} + return &netinterfaceCollector{ + logger: logger, + collisions: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "collisions_total"), + "Total number of CSMA collisions on the interface.", labels, nil, + ), + ibytes: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "receive_bytes_total"), + "Total number of bytes received on the interface.", labels, nil, + ), + ipackets: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "receive_packets_total"), + "Total number of packets received on the interface.", labels, nil, + ), + obytes: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "transmit_bytes_total"), + "Total number of bytes transmitted on the interface.", labels, nil, + ), + opackets: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "transmit_packets_total"), + "Total number of packets transmitted on the interface.", labels, nil, + ), + }, nil +} + +func (c *netinterfaceCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.NetIfaceStat() + if err != nil { + return err + } + + for _, stat := range stats { + iface := stat.Name + + ch <- prometheus.MustNewConstMetric(c.collisions, prometheus.CounterValue, float64(stat.Collisions), iface) + ch <- prometheus.MustNewConstMetric(c.ibytes, prometheus.CounterValue, float64(stat.IBytes), iface) + ch <- prometheus.MustNewConstMetric(c.ipackets, prometheus.CounterValue, float64(stat.IPackets), iface) + ch <- prometheus.MustNewConstMetric(c.obytes, prometheus.CounterValue, float64(stat.OBytes), iface) + ch <- prometheus.MustNewConstMetric(c.opackets, prometheus.CounterValue, float64(stat.OPackets), iface) + } + return nil +} diff --git a/collector/partition_aix.go b/collector/partition_aix.go new file mode 100644 index 00000000..3c54e0f4 --- /dev/null +++ b/collector/partition_aix.go @@ -0,0 +1,118 @@ +// Copyright 2025 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. + +//go:build !nopartition +// +build !nopartition + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +type partitionCollector struct { + logger *slog.Logger + entitledCapacity *prometheus.Desc + memoryMax *prometheus.Desc + memoryOnline *prometheus.Desc + cpuOnline *prometheus.Desc + cpuSys *prometheus.Desc + cpuPool *prometheus.Desc + powerSaveMode *prometheus.Desc + smtThreads *prometheus.Desc +} + +const ( + partitionCollectorSubsystem = "partition" +) + +func init() { + registerCollector("partition", defaultEnabled, NewPartitionCollector) +} + +func NewPartitionCollector(logger *slog.Logger) (Collector, error) { + return &partitionCollector{ + logger: logger, + entitledCapacity: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "entitled_capacity"), + "Entitled processor capacity of the partition in CPU units (e.g. 1.0 = one core).", + nil, nil, + ), + memoryMax: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "memory_max"), + "Maximum memory of the partition in bytes.", + nil, nil, + ), + memoryOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "memory_online"), + "Online memory of the partition in bytes.", + nil, nil, + ), + cpuOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_online"), + "Number of online CPUs in the partition.", + nil, nil, + ), + cpuSys: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_sys"), + "Number of physical CPUs in the system.", + nil, nil, + ), + cpuPool: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_pool"), + "Number of physical CPUs in the pool.", + nil, nil, + ), + powerSaveMode: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "power_save_mode"), + "Power save mode of the partition (1 for enabled, 0 for disabled).", + nil, nil, + ), + smtThreads: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "smt_threads"), + "Number of SMT threads per core.", + nil, nil, + ), + }, nil +} + +func (c *partitionCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.PartitionStat() + if err != nil { + return err + } + + powerSaveMode := 0.0 + if stats.Conf.PowerSave { + powerSaveMode = 1.0 + } + + ch <- prometheus.MustNewConstMetric(c.entitledCapacity, prometheus.GaugeValue, float64(stats.EntCapacity)/100.0) + + ch <- prometheus.MustNewConstMetric(c.memoryMax, prometheus.GaugeValue, float64(stats.Mem.Max)*1024*1024) + ch <- prometheus.MustNewConstMetric(c.memoryOnline, prometheus.GaugeValue, float64(stats.Mem.Online)*1024*1024) + + ch <- prometheus.MustNewConstMetric(c.cpuOnline, prometheus.GaugeValue, float64(stats.VCpus.Online)) + + ch <- prometheus.MustNewConstMetric(c.cpuSys, prometheus.GaugeValue, float64(stats.NumProcessors.Online)) + + ch <- prometheus.MustNewConstMetric(c.cpuPool, prometheus.GaugeValue, float64(stats.ActiveCpusInPool)) + + ch <- prometheus.MustNewConstMetric(c.powerSaveMode, prometheus.GaugeValue, powerSaveMode) + ch <- prometheus.MustNewConstMetric(c.smtThreads, prometheus.GaugeValue, float64(stats.SmtThreads)) + + return nil +} diff --git a/collector/perf_linux_test.go b/collector/perf_linux_test.go index e3858511..7560b0a0 100644 --- a/collector/perf_linux_test.go +++ b/collector/perf_linux_test.go @@ -33,7 +33,7 @@ func canTestPerf(t *testing.T) { if err != nil { t.Skip("Procfs not mounted, skipping perf tests") } - paranoidStr := strings.Replace(string(paranoidBytes), "\n", "", -1) + paranoidStr := strings.ReplaceAll(string(paranoidBytes), "\n", "") paranoid, err := strconv.Atoi(paranoidStr) if err != nil { t.Fatalf("Expected perf_event_paranoid to be an int, got: %s", paranoidStr) diff --git a/collector/processes_linux.go b/collector/processes_linux.go index 653045e5..add7f0df 100644 --- a/collector/processes_linux.go +++ b/collector/processes_linux.go @@ -106,7 +106,7 @@ func (c *processCollector) Update(ch chan<- prometheus.Metric) error { pidM, err := readUintFromFile(procFilePath("sys/kernel/pid_max")) if err != nil { - return fmt.Errorf("unable to retrieve limit number of maximum pids alloved: %w", err) + return fmt.Errorf("unable to retrieve limit number of maximum pids allowed: %w", err) } ch <- prometheus.MustNewConstMetric(c.pidUsed, prometheus.GaugeValue, float64(pids)) ch <- prometheus.MustNewConstMetric(c.pidMax, prometheus.GaugeValue, float64(pidM)) diff --git a/collector/processes_linux_test.go b/collector/processes_linux_test.go index c50d16c8..f89ad12f 100644 --- a/collector/processes_linux_test.go +++ b/collector/processes_linux_test.go @@ -48,7 +48,7 @@ func TestReadProcessStatus(t *testing.T) { } maxPid, err := readUintFromFile(procFilePath("sys/kernel/pid_max")) if err != nil { - t.Fatalf("Unable to retrieve limit number of maximum pids alloved %v\n", err) + t.Fatalf("Unable to retrieve limit number of maximum pids allowed %v\n", err) } if uint64(pids) > maxPid || pids == 0 { t.Fatalf("Total running pids cannot be greater than %d or equals to 0", maxPid) diff --git a/collector/systemd_linux_test.go b/collector/systemd_linux_test.go index 1c290377..f12b6ef8 100644 --- a/collector/systemd_linux_test.go +++ b/collector/systemd_linux_test.go @@ -122,11 +122,12 @@ func TestSystemdSummary(t *testing.T) { summary := summarizeUnits(fixtures[0]) for _, state := range unitStatesName { - if state == "inactive" { + switch state { + case "inactive": testSummaryHelper(t, state, summary[state], 3.0) - } else if state == "active" { + case "active": testSummaryHelper(t, state, summary[state], 1.0) - } else { + default: testSummaryHelper(t, state, summary[state], 0.0) } } diff --git a/collector/zfs_linux.go b/collector/zfs_linux.go index 4baf2b35..ff544a79 100644 --- a/collector/zfs_linux.go +++ b/collector/zfs_linux.go @@ -435,5 +435,5 @@ type zfsSysctl string func (s zfsSysctl) metricName() string { parts := strings.Split(string(s), ".") - return strings.Replace(parts[len(parts)-1], "-", "_", -1) + return strings.ReplaceAll(parts[len(parts)-1], "-", "_") } diff --git a/go.mod b/go.mod index c7a2580a..23a4923f 100644 --- a/go.mod +++ b/go.mod @@ -18,18 +18,18 @@ require ( github.com/mattn/go-xmlrpc v0.0.3 github.com/mdlayher/ethtool v0.2.0 github.com/mdlayher/netlink v1.7.2 - github.com/mdlayher/wifi v0.3.1 + github.com/mdlayher/wifi v0.5.0 github.com/opencontainers/selinux v1.11.1 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 github.com/prometheus-community/go-runit v0.1.0 github.com/prometheus/client_golang v1.21.1 - github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.62.0 + github.com/prometheus/client_model v0.6.2 + github.com/prometheus/common v0.64.0 github.com/prometheus/exporter-toolkit v0.14.0 - github.com/prometheus/procfs v0.16.0 + github.com/prometheus/procfs v0.16.1 github.com/safchain/ethtool v0.5.10 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - golang.org/x/sys v0.30.0 + golang.org/x/sys v0.33.0 howett.net/plist v1.0.1 ) @@ -51,11 +51,11 @@ require ( github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.35.0 // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/text v0.22.0 // indirect - google.golang.org/protobuf v1.36.1 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/text v0.25.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index ef94789f..73f3cbc5 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/mdlayher/wifi v0.3.1 h1:bZDuMI1f7z5BtUUO3NgHRdR/R88YtywIe6dsEFI0Txs= -github.com/mdlayher/wifi v0.3.1/go.mod h1:ODQaObvsglghTuNhezD9grkTB4shVNc28aJfTXmvSi8= +github.com/mdlayher/wifi v0.5.0 h1:TGZIcrhL6h3710amshpEJnMzLs74MrZOF+8qbm8Gx/I= +github.com/mdlayher/wifi v0.5.0/go.mod h1:yfQs+5zr1eOIfdsWDcZonWdznnt/Iiz0/4772cfZuHk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= @@ -77,14 +77,14 @@ github.com/prometheus-community/go-runit v0.1.0 h1:uTWEj/Fn2RoLdfg/etSqwzgYNOYPr github.com/prometheus-community/go-runit v0.1.0/go.mod h1:AvJ9Jo3gAFu2lbM4+qfjdpq30FfiLDJZKbQ015u08IQ= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg= github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA= -github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= -github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/safchain/ethtool v0.5.10 h1:Im294gZtuf4pSGJRAOGKaASNi3wMeFaGaWuSaomedpc= @@ -102,25 +102,25 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/node_exporter.go b/node_exporter.go index 88441e1e..22939cbc 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -112,7 +112,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err != nil { h.logger.Warn("Couldn't create filtered metrics handler:", "err", err) w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err))) + fmt.Fprintf(w, "Couldn't create filtered metrics handler: %s", err) return } filteredHandler.ServeHTTP(w, r)