diff --git a/.circleci/config.yml b/.circleci/config.yml index 3715e300..96b38cff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,10 +7,10 @@ executors: # should also be updated. golang: docker: - - image: cimg/go:1.21 + - image: cimg/go:1.23 arm: - machine: - image: ubuntu-2204:current + docker: + - image: cimg/go:1.23 resource_class: arm.medium jobs: @@ -25,7 +25,7 @@ jobs: test-arm: executor: arm steps: - - checkout + - prometheus/setup_environment - run: uname -a - run: make test-e2e test_mixins: @@ -49,6 +49,16 @@ jobs: - run: docker run --privileged linuxkit/binfmt:af88a591f9cc896a52ce596b9cf7ca26a061ef97 - run: promu crossbuild -v --parallelism $CIRCLE_NODE_TOTAL --parallelism-thread $CIRCLE_NODE_INDEX - run: promu --config .promu-cgo.yml crossbuild -v --parallelism $CIRCLE_NODE_TOTAL --parallelism-thread $CIRCLE_NODE_INDEX + # sign the darwin build so it doesn't get SIGKILLed on start, see: https://github.com/prometheus/node_exporter/issues/2539 + - run: + command: | + if [[ -f "$(pwd)/.build/darwin-arm64/node_exporter" ]]; then + promu codesign "$(pwd)/.build/darwin-arm64/node_exporter" + fi + + if [[ -f "$(pwd)/.build/darwin-amd64/node_exporter" ]]; then + promu codesign "$(pwd)/.build/darwin-amd64/node_exporter" + fi - persist_to_workspace: root: . paths: @@ -60,7 +70,7 @@ jobs: machine: image: ubuntu-2204:current environment: - DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.21-base + DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.23-base REPO_PATH: github.com/prometheus/node_exporter steps: - prometheus/setup_environment diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index 8a57107d..8ddbc34a 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -4,6 +4,7 @@ on: push: paths: - "README.md" + - "README-containers.md" - ".github/workflows/container_description.yml" branches: [ main, master ] @@ -17,7 +18,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Set docker hub repo name run: echo "DOCKER_REPO_NAME=$(make docker-repo-name)" >> $GITHUB_ENV - name: Push README to Dockerhub @@ -29,7 +30,9 @@ jobs: destination_container_repo: ${{ env.DOCKER_REPO_NAME }} provider: dockerhub short_description: ${{ env.DOCKER_REPO_NAME }} - readme_file: 'README.md' + # Empty string results in README-containers.md being pushed if it + # exists. Otherwise, README.md is pushed. + readme_file: '' PushQuayIoReadme: runs-on: ubuntu-latest @@ -37,7 +40,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Set quay.io org name run: echo "DOCKER_REPO=$(echo quay.io/${GITHUB_REPOSITORY_OWNER} | tr -d '-')" >> $GITHUB_ENV - name: Set quay.io repo name @@ -49,4 +52,6 @@ jobs: with: destination_container_repo: ${{ env.DOCKER_REPO_NAME }} provider: quay - readme_file: 'README.md' + # Empty string results in README-containers.md being pushed if it + # exists. Otherwise, README.md is pushed. + readme_file: '' diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 4dc7b830..1c099932 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,15 +24,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: install Go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - name: Install Go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.22.x + go-version: 1.23.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - name: Lint - uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0 + uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 with: - version: v1.55.2 + args: --verbose + version: v1.60.2 diff --git a/.golangci.yml b/.golangci.yml index 472b3a5e..1f1b0f63 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,8 +32,6 @@ linters-settings: exclude-functions: # Used in HTTP handlers, any error is handled by the server itself. - (net/http.ResponseWriter).Write - # Never check for logger errors. - - (github.com/go-kit/log.Logger).Log revive: rules: # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter diff --git a/.promu-cgo.yml b/.promu-cgo.yml index d6f82529..542f51e2 100644 --- a/.promu-cgo.yml +++ b/.promu-cgo.yml @@ -1,14 +1,13 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu.yml should also be updated. - version: 1.21 + version: 1.23 cgo: true repository: path: github.com/prometheus/node_exporter build: binaries: - name: node_exporter - flags: -a -tags 'netgo osusergo static_build' ldflags: | -X github.com/prometheus/common/version.Version={{.Version}} -X github.com/prometheus/common/version.Revision={{.Revision}} diff --git a/.promu.yml b/.promu.yml index d0a66635..fcba92d0 100644 --- a/.promu.yml +++ b/.promu.yml @@ -1,13 +1,12 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu-cgo.yml should also be updated. - version: 1.21 + version: 1.23 repository: path: github.com/prometheus/node_exporter build: binaries: - name: node_exporter - flags: -a -tags 'netgo osusergo static_build' ldflags: | -X github.com/prometheus/common/version.Version={{.Version}} -X github.com/prometheus/common/version.Revision={{.Revision}} diff --git a/.yamllint b/.yamllint index 955a5a62..1859cb62 100644 --- a/.yamllint +++ b/.yamllint @@ -1,5 +1,7 @@ --- extends: default +ignore: | + ui/react-app/node_modules rules: braces: diff --git a/CHANGELOG.md b/CHANGELOG.md index 096c0b10..79ad9270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,34 @@ * [ENHANCEMENT] * [BUGFIX] +## 1.8.1 / 2024-05-16 + +* [BUGFIX] Fix CPU seconds on Solaris #2963 +* [BUGFIX] Sign Darwin/MacOS binaries #3008 +* [BUGFIX] Fix pressure collector nil reference #3016 + +## 1.8.0 / 2024-04-24 + +* [CHANGE] exec_bsd: Fix labels for `vm.stats.sys.v_syscall` sysctl #2895 +* [CHANGE] diskstats: Ignore zram devices on linux systems #2898 +* [CHANGE] textfile: Avoid inconsistent help-texts #2962 +* [CHANGE] os: Removed caching of modtime/filename of os-release file #2987 +* [FEATURE] xfrm: Add new collector #2866 +* [FEATURE] watchdog: Add new collector #2880 +* [ENHANCEMENT] cpu_vulnerabilities: Add mitigation information label #2806 +* [ENHANCEMENT] nfsd: Handle new `wdeleg_getattr` attribute #2810 +* [ENHANCEMENT] netstat: Add TCPOFOQueue to default netstat metrics #2867 +* [ENHANCEMENT] filesystem: surface device errors #2923 +* [ENHANCEMENT] os: Add support end parsing #2982 +* [ENHANCEMENT] zfs: Log mib when sysctl read fails on FreeBSD #2975 +* [ENHANCEMENT] fibre_channel: update procfs to take into account optional attributes #2933 +* [BUGFIX] cpu: Fix debug log in cpu collector #2857 +* [BUGFIX] hwmon: Fix hwmon nil ptr #2873 +* [BUGFIX] hwmon: Fix hwmon error capture #2915 +* [BUGFIX] zfs: Revert "Add ZFS freebsd per dataset stats #2925 +* [BUGFIX] ethtool: Sanitize ethtool metric name keys #2940 +* [BUGFIX] fix: data race of NetClassCollector metrics initialization #2995 + ## 1.7.0 / 2023-11-11 * [FEATURE] Add ZFS freebsd per dataset stats #2753 diff --git a/Makefile.common b/Makefile.common index 49ed5f54..cbb5d863 100644 --- a/Makefile.common +++ b/Makefile.common @@ -49,19 +49,19 @@ endif GOTEST := $(GO) test GOTEST_DIR := ifneq ($(CIRCLE_JOB),) -ifneq ($(shell command -v gotestsum > /dev/null),) +ifneq ($(shell command -v gotestsum 2> /dev/null),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif -PROMU_VERSION ?= 0.15.0 +PROMU_VERSION ?= 0.17.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.55.2 +GOLANGCI_LINT_VERSION ?= v1.60.2 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) @@ -182,7 +182,7 @@ endif .PHONY: common-yamllint common-yamllint: @echo ">> running yamllint on all YAML files in the repository" -ifeq (, $(shell command -v yamllint > /dev/null)) +ifeq (, $(shell command -v yamllint 2> /dev/null)) @echo "yamllint not installed so skipping" else yamllint . @@ -275,3 +275,9 @@ $(1)_precheck: exit 1; \ fi endef + +govulncheck: install-govulncheck + govulncheck ./... + +install-govulncheck: + command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest diff --git a/README.md b/README.md index ad385650..44a9d2b1 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,11 @@ ethtool | metrics | --collector.ethtool.metrics-include | N/A filesystem | fs-types | N/A | --collector.filesystem.fs-types-exclude filesystem | mount-points | N/A | --collector.filesystem.mount-points-exclude hwmon | chip | --collector.hwmon.chip-include | --collector.hwmon.chip-exclude +hwmon | sensor | --collector.hwmon.sensor-include | --collector.hwmon.sensor-exclude +interrupts | name | --collector.interrupts.name-include | --collector.interrupts.name-exclude netdev | device | --collector.netdev.device-include | --collector.netdev.device-exclude qdisk | device | --collector.qdisk.device-include | --collector.qdisk.device-exclude +slabinfo | slab-names | --collector.slabinfo.slabs-include | --collector.slabinfo.slabs-exclude sysctl | all | --collector.sysctl.include | N/A systemd | unit | --collector.systemd.unit-include | --collector.systemd.unit-exclude @@ -336,13 +339,21 @@ mv /path/to/directory/role.prom.$$ /path/to/directory/role.prom The `node_exporter` will expose all metrics from enabled collectors by default. This is the recommended way to collect metrics to avoid errors when comparing metrics of different families. -For advanced use the `node_exporter` can be passed an optional list of collectors to filter metrics. The `collect[]` parameter may be used multiple times. In Prometheus configuration you can use this syntax under the [scrape config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#). +For advanced use the `node_exporter` can be passed an optional list of collectors to filter metrics. The parameters `collect[]` and `exclude[]` can be used multiple times (but cannot be combined). In Prometheus configuration you can use this syntax under the [scrape config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#). +Collect only `cpu` and `meminfo` collector metrics: ``` params: collect[]: - - foo - - bar + - cpu + - meminfo +``` + +Collect all enabled collector metrics but exclude `netdev`: +``` + params: + exclude[]: + - netdev ``` This can be useful for having different Prometheus servers collect specific metrics from nodes. @@ -371,7 +382,7 @@ To see all available configuration flags: ## TLS endpoint -** EXPERIMENTAL ** +**EXPERIMENTAL** The exporter supports TLS via a new web configuration file. diff --git a/VERSION b/VERSION index bd8bf882..a8fdfda1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.0 +1.8.1 diff --git a/collector/arp_linux.go b/collector/arp_linux.go index c7861156..7f417d82 100644 --- a/collector/arp_linux.go +++ b/collector/arp_linux.go @@ -19,11 +19,11 @@ package collector import ( "errors" "fmt" + "log/slog" "net" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" "golang.org/x/sys/unix" @@ -39,7 +39,7 @@ type arpCollector struct { fs procfs.FS deviceFilter deviceFilter entries *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -47,7 +47,7 @@ func init() { } // NewARPCollector returns a new Collector exposing ARP stats. -func NewARPCollector(logger log.Logger) (Collector, error) { +func NewARPCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/bcache_linux.go b/collector/bcache_linux.go index 1d402d3f..97064e21 100644 --- a/collector/bcache_linux.go +++ b/collector/bcache_linux.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/bcache" ) @@ -36,12 +36,12 @@ func init() { // A bcacheCollector is a Collector which gathers metrics from Linux bcache. type bcacheCollector struct { fs bcache.FS - logger log.Logger + logger *slog.Logger } // NewBcacheCollector returns a newly allocated bcacheCollector. // It exposes a number of Linux bcache statistics. -func NewBcacheCollector(logger log.Logger) (Collector, error) { +func NewBcacheCollector(logger *slog.Logger) (Collector, error) { fs, err := bcache.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) diff --git a/collector/bonding_linux.go b/collector/bonding_linux.go index d9d04e22..bfec32fd 100644 --- a/collector/bonding_linux.go +++ b/collector/bonding_linux.go @@ -19,18 +19,17 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "path/filepath" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) type bondingCollector struct { slaves, active typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -39,7 +38,7 @@ func init() { // NewBondingCollector returns a newly allocated bondingCollector. // It exposes the number of configured and active slave of linux bonding interfaces. -func NewBondingCollector(logger log.Logger) (Collector, error) { +func NewBondingCollector(logger *slog.Logger) (Collector, error) { return &bondingCollector{ slaves: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "bonding", "slaves"), @@ -61,7 +60,7 @@ func (c *bondingCollector) Update(ch chan<- prometheus.Metric) error { bondingStats, err := readBondingStats(statusfile) if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "Not collecting bonding, file does not exist", "file", statusfile) + c.logger.Debug("Not collecting bonding, file does not exist", "file", statusfile) return ErrNoData } return err diff --git a/collector/boot_time_bsd.go b/collector/boot_time_bsd.go index 8a9c17b3..d632fef0 100644 --- a/collector/boot_time_bsd.go +++ b/collector/boot_time_bsd.go @@ -18,13 +18,13 @@ package collector import ( - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" + "log/slog" ) type bootTimeCollector struct { - logger log.Logger + logger *slog.Logger } func init() { @@ -32,7 +32,7 @@ func init() { } // newBootTimeCollector returns a new Collector exposing system boot time on BSD systems. -func newBootTimeCollector(logger log.Logger) (Collector, error) { +func newBootTimeCollector(logger *slog.Logger) (Collector, error) { return &bootTimeCollector{ logger: logger, }, nil diff --git a/collector/boot_time_solaris.go b/collector/boot_time_solaris.go index 15955121..4c336fb3 100644 --- a/collector/boot_time_solaris.go +++ b/collector/boot_time_solaris.go @@ -17,21 +17,21 @@ package collector import ( - "github.com/go-kit/log" "github.com/illumos/go-kstat" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) type bootTimeCollector struct { boottime typedDesc - logger log.Logger + logger *slog.Logger } func init() { registerCollector("boottime", defaultEnabled, newBootTimeCollector) } -func newBootTimeCollector(logger log.Logger) (Collector, error) { +func newBootTimeCollector(logger *slog.Logger) (Collector, error) { return &bootTimeCollector{ boottime: typedDesc{ prometheus.NewDesc( diff --git a/collector/btrfs_linux.go b/collector/btrfs_linux.go index f1490541..2dbdf5aa 100644 --- a/collector/btrfs_linux.go +++ b/collector/btrfs_linux.go @@ -18,13 +18,12 @@ package collector import ( "fmt" + "log/slog" "path" "strings" "syscall" dennwc "github.com/dennwc/btrfs" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/btrfs" ) @@ -32,7 +31,7 @@ import ( // A btrfsCollector is a Collector which gathers metrics from Btrfs filesystems. type btrfsCollector struct { fs btrfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -40,7 +39,7 @@ func init() { } // NewBtrfsCollector returns a new Collector exposing Btrfs statistics. -func NewBtrfsCollector(logger log.Logger) (Collector, error) { +func NewBtrfsCollector(logger *slog.Logger) (Collector, error) { fs, err := btrfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -62,8 +61,8 @@ func (c *btrfsCollector) Update(ch chan<- prometheus.Metric) error { ioctlStatsMap, err := c.getIoctlStats() if err != nil { - level.Debug(c.logger).Log( - "msg", "Error querying btrfs device stats with ioctl", + c.logger.Debug( + "Error querying btrfs device stats with ioctl", "err", err) ioctlStatsMap = make(map[string]*btrfsIoctlFsStats) } @@ -129,8 +128,8 @@ func (c *btrfsCollector) getIoctlStats() (map[string]*btrfsIoctlFsStats, error) if err != nil { // Failed to open this mount point, maybe we didn't have permission // maybe we'll find another mount point for this FS later. - level.Debug(c.logger).Log( - "msg", "Error inspecting btrfs mountpoint", + c.logger.Debug( + "Error inspecting btrfs mountpoint", "mountPoint", mountPath, "err", err) continue @@ -141,8 +140,8 @@ func (c *btrfsCollector) getIoctlStats() (map[string]*btrfsIoctlFsStats, error) if err != nil { // Failed to get the FS info for some reason, // perhaps it'll work with a different mount point - level.Debug(c.logger).Log( - "msg", "Error querying btrfs filesystem", + c.logger.Debug( + "Error querying btrfs filesystem", "mountPoint", mountPath, "err", err) continue @@ -156,8 +155,8 @@ func (c *btrfsCollector) getIoctlStats() (map[string]*btrfsIoctlFsStats, error) deviceStats, err := c.getIoctlDeviceStats(fs, &fsInfo) if err != nil { - level.Debug(c.logger).Log( - "msg", "Error querying btrfs device stats", + c.logger.Debug( + "Error querying btrfs device stats", "mountPoint", mountPath, "err", err) continue @@ -275,6 +274,30 @@ func (c *btrfsCollector) getMetrics(s *btrfs.Stats, ioctlStats *btrfsIoctlFsStat metricType: prometheus.GaugeValue, value: float64(s.Allocation.GlobalRsvSize), }, + { + name: "commits_total", + desc: "The total number of commits that have occurred.", + metricType: prometheus.CounterValue, + value: float64(s.CommitStats.Commits), + }, + { + name: "last_commit_seconds", + desc: "Duration of the most recent commit, in seconds.", + metricType: prometheus.GaugeValue, + value: float64(s.CommitStats.LastCommitMs) / 1000, + }, + { + name: "max_commit_seconds", + desc: "Duration of the slowest commit, in seconds.", + metricType: prometheus.GaugeValue, + value: float64(s.CommitStats.MaxCommitMs) / 1000, + }, + { + name: "commit_seconds_total", + desc: "Sum of the duration of all commits, in seconds.", + metricType: prometheus.CounterValue, + value: float64(s.CommitStats.TotalCommitMs) / 1000, + }, } // Information about data, metadata and system data. diff --git a/collector/btrfs_linux_test.go b/collector/btrfs_linux_test.go index fcd6f9f9..c2ed57e5 100644 --- a/collector/btrfs_linux_test.go +++ b/collector/btrfs_linux_test.go @@ -27,6 +27,10 @@ var expectedBtrfsMetrics = [][]btrfsMetric{ { {name: "info", value: 1, extraLabel: []string{"label"}, extraLabelValue: []string{"fixture"}}, {name: "global_rsv_size_bytes", value: 1.6777216e+07}, + {name: "commits_total", value: 258051, metricType: 1}, + {name: "last_commit_seconds", value: 1.0}, + {name: "max_commit_seconds", value: 51.462}, + {name: "commit_seconds_total", value: 47836.090, metricType: 1}, {name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"data"}}, {name: "used_bytes", value: 8.08189952e+08, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid0"}}, {name: "size_bytes", value: 2.147483648e+09, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid0"}}, @@ -45,6 +49,10 @@ var expectedBtrfsMetrics = [][]btrfsMetric{ { {name: "info", value: 1, extraLabel: []string{"label"}, extraLabelValue: []string{""}}, {name: "global_rsv_size_bytes", value: 1.6777216e+07}, + {name: "commits_total", value: 0, metricType: 1}, + {name: "last_commit_seconds", value: 0}, + {name: "max_commit_seconds", value: 0}, + {name: "commit_seconds_total", value: 0, metricType: 1}, {name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"data"}}, {name: "used_bytes", value: 0, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid5"}}, {name: "size_bytes", value: 6.44087808e+08, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid5"}}, @@ -92,7 +100,10 @@ func checkMetric(exp, got *btrfsMetric) bool { } func TestBtrfs(t *testing.T) { - fs, _ := btrfs.NewFS("fixtures/sys") + fs, err := btrfs.NewFS("fixtures/sys") + if err != nil { + t.Fatal(err) + } collector := &btrfsCollector{fs: fs} stats, err := collector.fs.Stats() diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go index c3cc5e04..62392b96 100644 --- a/collector/buddyinfo.go +++ b/collector/buddyinfo.go @@ -18,10 +18,9 @@ package collector import ( "fmt" + "log/slog" "strconv" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -33,7 +32,7 @@ const ( type buddyinfoCollector struct { fs procfs.FS desc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -41,7 +40,7 @@ func init() { } // NewBuddyinfoCollector returns a new Collector exposing buddyinfo stats. -func NewBuddyinfoCollector(logger log.Logger) (Collector, error) { +func NewBuddyinfoCollector(logger *slog.Logger) (Collector, error) { desc := prometheus.NewDesc( prometheus.BuildFQName(namespace, buddyInfoSubsystem, "blocks"), "Count of free blocks according to size.", @@ -62,7 +61,7 @@ func (c *buddyinfoCollector) Update(ch chan<- prometheus.Metric) error { return fmt.Errorf("couldn't get buddyinfo: %w", err) } - level.Debug(c.logger).Log("msg", "Set node_buddy", "buddyInfo", buddyInfo) + c.logger.Debug("Set node_buddy", "buddyInfo", buddyInfo) for _, entry := range buddyInfo { for size, value := range entry.Sizes { ch <- prometheus.MustNewConstMetric( diff --git a/collector/cgroups_linux.go b/collector/cgroups_linux.go index 9f6d01a4..2a4e288e 100644 --- a/collector/cgroups_linux.go +++ b/collector/cgroups_linux.go @@ -18,8 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -30,7 +30,7 @@ type cgroupSummaryCollector struct { fs procfs.FS cgroups *prometheus.Desc enabled *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -38,7 +38,7 @@ func init() { } // NewCgroupSummaryCollector returns a new Collector exposing a summary of cgroups. -func NewCgroupSummaryCollector(logger log.Logger) (Collector, error) { +func NewCgroupSummaryCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/collector.go b/collector/collector.go index 3112c789..9768fb6a 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -17,12 +17,11 @@ package collector import ( "errors" "fmt" + "log/slog" "sync" "time" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -50,14 +49,14 @@ const ( ) var ( - factories = make(map[string]func(logger log.Logger) (Collector, error)) + factories = make(map[string]func(logger *slog.Logger) (Collector, error)) initiatedCollectorsMtx = sync.Mutex{} initiatedCollectors = make(map[string]Collector) collectorState = make(map[string]*bool) forcedCollectors = map[string]bool{} // collectors which have been explicitly enabled or disabled ) -func registerCollector(collector string, isDefaultEnabled bool, factory func(logger log.Logger) (Collector, error)) { +func registerCollector(collector string, isDefaultEnabled bool, factory func(logger *slog.Logger) (Collector, error)) { var helpDefaultState string if isDefaultEnabled { helpDefaultState = "enabled" @@ -78,7 +77,7 @@ func registerCollector(collector string, isDefaultEnabled bool, factory func(log // NodeCollector implements the prometheus.Collector interface. type NodeCollector struct { Collectors map[string]Collector - logger log.Logger + logger *slog.Logger } // DisableDefaultCollectors sets the collector state to false for all collectors which @@ -104,7 +103,7 @@ func collectorFlagAction(collector string) func(ctx *kingpin.ParseContext) error } // NewNodeCollector creates a new NodeCollector. -func NewNodeCollector(logger log.Logger, filters ...string) (*NodeCollector, error) { +func NewNodeCollector(logger *slog.Logger, filters ...string) (*NodeCollector, error) { f := make(map[string]bool) for _, filter := range filters { enabled, exist := collectorState[filter] @@ -126,7 +125,7 @@ func NewNodeCollector(logger log.Logger, filters ...string) (*NodeCollector, err if collector, ok := initiatedCollectors[key]; ok { collectors[key] = collector } else { - collector, err := factories[key](log.With(logger, "collector", key)) + collector, err := factories[key](logger.With("collector", key)) if err != nil { return nil, err } @@ -156,7 +155,7 @@ func (n NodeCollector) Collect(ch chan<- prometheus.Metric) { wg.Wait() } -func execute(name string, c Collector, ch chan<- prometheus.Metric, logger log.Logger) { +func execute(name string, c Collector, ch chan<- prometheus.Metric, logger *slog.Logger) { begin := time.Now() err := c.Update(ch) duration := time.Since(begin) @@ -164,13 +163,13 @@ func execute(name string, c Collector, ch chan<- prometheus.Metric, logger log.L if err != nil { if IsNoDataError(err) { - level.Debug(logger).Log("msg", "collector returned no data", "name", name, "duration_seconds", duration.Seconds(), "err", err) + logger.Debug("collector returned no data", "name", name, "duration_seconds", duration.Seconds(), "err", err) } else { - level.Error(logger).Log("msg", "collector failed", "name", name, "duration_seconds", duration.Seconds(), "err", err) + logger.Error("collector failed", "name", name, "duration_seconds", duration.Seconds(), "err", err) } success = 0 } else { - level.Debug(logger).Log("msg", "collector succeeded", "name", name, "duration_seconds", duration.Seconds()) + logger.Debug("collector succeeded", "name", name, "duration_seconds", duration.Seconds()) success = 1 } ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), name) diff --git a/collector/conntrack_linux.go b/collector/conntrack_linux.go index e4ea9549..309857c7 100644 --- a/collector/conntrack_linux.go +++ b/collector/conntrack_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -38,7 +37,7 @@ type conntrackCollector struct { drop *prometheus.Desc earlyDrop *prometheus.Desc searchRestart *prometheus.Desc - logger log.Logger + logger *slog.Logger } type conntrackStatistics struct { @@ -57,7 +56,7 @@ func init() { } // NewConntrackCollector returns a new Collector exposing conntrack stats. -func NewConntrackCollector(logger log.Logger) (Collector, error) { +func NewConntrackCollector(logger *slog.Logger) (Collector, error) { return &conntrackCollector{ current: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "nf_conntrack_entries"), @@ -154,7 +153,7 @@ func (c *conntrackCollector) Update(ch chan<- prometheus.Metric) error { func (c *conntrackCollector) handleErr(err error) error { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "conntrack probably not loaded") + c.logger.Debug("conntrack probably not loaded") return ErrNoData } return fmt.Errorf("failed to retrieve conntrack stats: %w", err) diff --git a/collector/cpu_aix.go b/collector/cpu_aix.go new file mode 100644 index 00000000..9d896e2d --- /dev/null +++ b/collector/cpu_aix.go @@ -0,0 +1,76 @@ +// Copyright 2024 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 !nocpu +// +build !nocpu + +package collector + +/* +#include // Include the standard Unix header +#include // For errno +*/ +import "C" +import ( + "fmt" + "log/slog" + "strconv" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +type cpuCollector struct { + cpu typedDesc + logger *slog.Logger + tickPerSecond int64 +} + +func init() { + registerCollector("cpu", defaultEnabled, NewCpuCollector) +} + +func tickPerSecond() (int64, 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 +} + +func NewCpuCollector(logger *slog.Logger) (Collector, error) { + ticks, err := tickPerSecond() + if err != nil { + return nil, err + } + return &cpuCollector{ + cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + logger: logger, + tickPerSecond: ticks, + }, nil +} + +func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.CpuStat() + if err != nil { + return err + } + + 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") + } + return nil +} diff --git a/collector/cpu_darwin.go b/collector/cpu_darwin.go index 6c461cc3..31b11702 100644 --- a/collector/cpu_darwin.go +++ b/collector/cpu_darwin.go @@ -23,10 +23,10 @@ import ( "bytes" "encoding/binary" "fmt" + "log/slog" "strconv" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -53,7 +53,7 @@ const ClocksPerSec = float64(C.CLK_TCK) type statCollector struct { cpu *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -61,7 +61,7 @@ func init() { } // NewCPUCollector returns a new Collector exposing CPU stats. -func NewCPUCollector(logger log.Logger) (Collector, error) { +func NewCPUCollector(logger *slog.Logger) (Collector, error) { return &statCollector{ cpu: nodeCPUSecondsDesc, logger: logger, diff --git a/collector/cpu_dragonfly.go b/collector/cpu_dragonfly.go index 61cba1ee..c53aaef0 100644 --- a/collector/cpu_dragonfly.go +++ b/collector/cpu_dragonfly.go @@ -18,10 +18,10 @@ package collector import ( "errors" + "log/slog" "strconv" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -78,7 +78,7 @@ const maxCPUTimesLen = C.MAXCPU * C.CPUSTATES type statCollector struct { cpu *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -86,7 +86,7 @@ func init() { } // NewStatCollector returns a new Collector exposing CPU stats. -func NewStatCollector(logger log.Logger) (Collector, error) { +func NewStatCollector(logger *slog.Logger) (Collector, error) { return &statCollector{ cpu: nodeCPUSecondsDesc, logger: logger, diff --git a/collector/cpu_freebsd.go b/collector/cpu_freebsd.go index 96b0f033..39b01185 100644 --- a/collector/cpu_freebsd.go +++ b/collector/cpu_freebsd.go @@ -18,12 +18,11 @@ package collector import ( "fmt" + "log/slog" "math" "strconv" "unsafe" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -85,7 +84,7 @@ func getCPUTimes() ([]cputime, error) { type statCollector struct { cpu typedDesc temp typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -93,7 +92,7 @@ func init() { } // NewStatCollector returns a new Collector exposing CPU stats. -func NewStatCollector(logger log.Logger) (Collector, error) { +func NewStatCollector(logger *slog.Logger) (Collector, error) { return &statCollector{ cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, temp: typedDesc{prometheus.NewDesc( @@ -134,11 +133,11 @@ func (c *statCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { if err == unix.ENOENT { // No temperature information for this CPU - level.Debug(c.logger).Log("msg", "no temperature information for CPU", "cpu", cpu) + c.logger.Debug("no temperature information for CPU", "cpu", cpu) } else { // Unexpected error ch <- c.temp.mustNewConstMetric(math.NaN(), lcpu) - level.Error(c.logger).Log("msg", "failed to query CPU temperature for CPU", "cpu", cpu, "err", err) + c.logger.Error("failed to query CPU temperature for CPU", "cpu", cpu, "err", err) } continue } diff --git a/collector/cpu_linux.go b/collector/cpu_linux.go index fcbf651b..1ee7b94d 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -18,20 +18,19 @@ package collector import ( "fmt" + "log/slog" "os" "path/filepath" "regexp" + "slices" "strconv" "sync" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" "github.com/prometheus/procfs/sysfs" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" ) type cpuCollector struct { @@ -45,7 +44,7 @@ type cpuCollector struct { cpuCoreThrottle *prometheus.Desc cpuPackageThrottle *prometheus.Desc cpuIsolated *prometheus.Desc - logger log.Logger + logger *slog.Logger cpuStats map[int64]procfs.CPUStat cpuStatsMutex sync.Mutex isolatedCpus []uint16 @@ -70,7 +69,7 @@ func init() { } // NewCPUCollector returns a new Collector exposing kernel/system statistics. -func NewCPUCollector(logger log.Logger) (Collector, error) { +func NewCPUCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -86,7 +85,7 @@ func NewCPUCollector(logger log.Logger) (Collector, error) { if !os.IsNotExist(err) { return nil, fmt.Errorf("Unable to get isolated cpus: %w", err) } - level.Debug(logger).Log("msg", "Could not open isolated file", "error", err) + logger.Debug("Could not open isolated file", "error", err) } c := &cpuCollector{ @@ -146,7 +145,7 @@ func NewCPUCollector(logger log.Logger) (Collector, error) { func (c *cpuCollector) compileIncludeFlags(flagsIncludeFlag, bugsIncludeFlag *string) error { if (*flagsIncludeFlag != "" || *bugsIncludeFlag != "") && !*enableCPUInfo { *enableCPUInfo = true - level.Info(c.logger).Log("msg", "--collector.cpu.info has been set to `true` because you set the following flags, like --collector.cpu.info.flags-include and --collector.cpu.info.bugs-include") + c.logger.Info("--collector.cpu.info has been set to `true` because you set the following flags, like --collector.cpu.info.flags-include and --collector.cpu.info.bugs-include") } var err error @@ -205,7 +204,7 @@ func (c *cpuCollector) updateInfo(ch chan<- prometheus.Metric) error { cpuFreqEnabled, ok := collectorState["cpufreq"] if !ok || cpuFreqEnabled == nil { - level.Debug(c.logger).Log("msg", "cpufreq key missing or nil value in collectorState map") + c.logger.Debug("cpufreq key missing or nil value in collectorState map") } else if !*cpuFreqEnabled { for _, cpu := range info { ch <- prometheus.MustNewConstMetric(c.cpuFrequencyHz, @@ -269,12 +268,12 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error // topology/physical_package_id if physicalPackageID, err = readUintFromFile(filepath.Join(cpu, "topology", "physical_package_id")); err != nil { - level.Debug(c.logger).Log("msg", "CPU is missing physical_package_id", "cpu", cpu) + c.logger.Debug("CPU is missing physical_package_id", "cpu", cpu) continue } // topology/core_id if coreID, err = readUintFromFile(filepath.Join(cpu, "topology", "core_id")); err != nil { - level.Debug(c.logger).Log("msg", "CPU is missing core_id", "cpu", cpu) + c.logger.Debug("CPU is missing core_id", "cpu", cpu) continue } @@ -292,7 +291,7 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error if coreThrottleCount, err := readUintFromFile(filepath.Join(cpu, "thermal_throttle", "core_throttle_count")); err == nil { packageCoreThrottles[physicalPackageID][coreID] = coreThrottleCount } else { - level.Debug(c.logger).Log("msg", "CPU is missing core_throttle_count", "cpu", cpu) + c.logger.Debug("CPU is missing core_throttle_count", "cpu", cpu) } } @@ -302,7 +301,7 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error if packageThrottleCount, err := readUintFromFile(filepath.Join(cpu, "thermal_throttle", "package_throttle_count")); err == nil { packageThrottles[physicalPackageID] = packageThrottleCount } else { - level.Debug(c.logger).Log("msg", "CPU is missing package_throttle_count", "cpu", cpu) + c.logger.Debug("CPU is missing package_throttle_count", "cpu", cpu) } } } @@ -380,68 +379,68 @@ func (c *cpuCollector) updateCPUStats(newStats map[int64]procfs.CPUStat) { // If idle jumps backwards by more than X seconds, assume we had a hotplug event and reset the stats for this CPU. if (cpuStats.Idle - n.Idle) >= jumpBackSeconds { - level.Debug(c.logger).Log("msg", jumpBackDebugMessage, "cpu", i, "old_value", cpuStats.Idle, "new_value", n.Idle) + c.logger.Debug(jumpBackDebugMessage, "cpu", i, "old_value", cpuStats.Idle, "new_value", n.Idle) cpuStats = procfs.CPUStat{} } if n.Idle >= cpuStats.Idle { cpuStats.Idle = n.Idle } else { - level.Debug(c.logger).Log("msg", "CPU Idle counter jumped backwards", "cpu", i, "old_value", cpuStats.Idle, "new_value", n.Idle) + c.logger.Debug("CPU Idle counter jumped backwards", "cpu", i, "old_value", cpuStats.Idle, "new_value", n.Idle) } if n.User >= cpuStats.User { cpuStats.User = n.User } else { - level.Debug(c.logger).Log("msg", "CPU User counter jumped backwards", "cpu", i, "old_value", cpuStats.User, "new_value", n.User) + c.logger.Debug("CPU User counter jumped backwards", "cpu", i, "old_value", cpuStats.User, "new_value", n.User) } if n.Nice >= cpuStats.Nice { cpuStats.Nice = n.Nice } else { - level.Debug(c.logger).Log("msg", "CPU Nice counter jumped backwards", "cpu", i, "old_value", cpuStats.Nice, "new_value", n.Nice) + c.logger.Debug("CPU Nice counter jumped backwards", "cpu", i, "old_value", cpuStats.Nice, "new_value", n.Nice) } if n.System >= cpuStats.System { cpuStats.System = n.System } else { - level.Debug(c.logger).Log("msg", "CPU System counter jumped backwards", "cpu", i, "old_value", cpuStats.System, "new_value", n.System) + c.logger.Debug("CPU System counter jumped backwards", "cpu", i, "old_value", cpuStats.System, "new_value", n.System) } if n.Iowait >= cpuStats.Iowait { cpuStats.Iowait = n.Iowait } else { - level.Debug(c.logger).Log("msg", "CPU Iowait counter jumped backwards", "cpu", i, "old_value", cpuStats.Iowait, "new_value", n.Iowait) + c.logger.Debug("CPU Iowait counter jumped backwards", "cpu", i, "old_value", cpuStats.Iowait, "new_value", n.Iowait) } if n.IRQ >= cpuStats.IRQ { cpuStats.IRQ = n.IRQ } else { - level.Debug(c.logger).Log("msg", "CPU IRQ counter jumped backwards", "cpu", i, "old_value", cpuStats.IRQ, "new_value", n.IRQ) + c.logger.Debug("CPU IRQ counter jumped backwards", "cpu", i, "old_value", cpuStats.IRQ, "new_value", n.IRQ) } if n.SoftIRQ >= cpuStats.SoftIRQ { cpuStats.SoftIRQ = n.SoftIRQ } else { - level.Debug(c.logger).Log("msg", "CPU SoftIRQ counter jumped backwards", "cpu", i, "old_value", cpuStats.SoftIRQ, "new_value", n.SoftIRQ) + c.logger.Debug("CPU SoftIRQ counter jumped backwards", "cpu", i, "old_value", cpuStats.SoftIRQ, "new_value", n.SoftIRQ) } if n.Steal >= cpuStats.Steal { cpuStats.Steal = n.Steal } else { - level.Debug(c.logger).Log("msg", "CPU Steal counter jumped backwards", "cpu", i, "old_value", cpuStats.Steal, "new_value", n.Steal) + c.logger.Debug("CPU Steal counter jumped backwards", "cpu", i, "old_value", cpuStats.Steal, "new_value", n.Steal) } if n.Guest >= cpuStats.Guest { cpuStats.Guest = n.Guest } else { - level.Debug(c.logger).Log("msg", "CPU Guest counter jumped backwards", "cpu", i, "old_value", cpuStats.Guest, "new_value", n.Guest) + c.logger.Debug("CPU Guest counter jumped backwards", "cpu", i, "old_value", cpuStats.Guest, "new_value", n.Guest) } if n.GuestNice >= cpuStats.GuestNice { cpuStats.GuestNice = n.GuestNice } else { - level.Debug(c.logger).Log("msg", "CPU GuestNice counter jumped backwards", "cpu", i, "old_value", cpuStats.GuestNice, "new_value", n.GuestNice) + c.logger.Debug("CPU GuestNice counter jumped backwards", "cpu", i, "old_value", cpuStats.GuestNice, "new_value", n.GuestNice) } c.cpuStats[i] = cpuStats diff --git a/collector/cpu_linux_test.go b/collector/cpu_linux_test.go index b148ffcd..158ae8ff 100644 --- a/collector/cpu_linux_test.go +++ b/collector/cpu_linux_test.go @@ -17,10 +17,11 @@ package collector import ( + "io" + "log/slog" "reflect" "testing" - "github.com/go-kit/log" "github.com/prometheus/procfs" ) @@ -35,7 +36,7 @@ func makeTestCPUCollector(s map[int64]procfs.CPUStat) *cpuCollector { dup := make(map[int64]procfs.CPUStat, len(s)) copyStats(dup, s) return &cpuCollector{ - logger: log.NewNopLogger(), + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), cpuStats: dup, } } diff --git a/collector/cpu_netbsd.go b/collector/cpu_netbsd.go index 7fc95c80..e7c8b1ce 100644 --- a/collector/cpu_netbsd.go +++ b/collector/cpu_netbsd.go @@ -18,6 +18,7 @@ package collector import ( "errors" + "log/slog" "math" "regexp" "sort" @@ -25,8 +26,6 @@ import ( "strings" "unsafe" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" @@ -214,7 +213,7 @@ func getCPUTimes() ([]cputime, error) { type statCollector struct { cpu typedDesc temp typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -222,7 +221,7 @@ func init() { } // NewStatCollector returns a new Collector exposing CPU stats. -func NewStatCollector(logger log.Logger) (Collector, error) { +func NewStatCollector(logger *slog.Logger) (Collector, error) { return &statCollector{ cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, temp: typedDesc{prometheus.NewDesc( @@ -269,7 +268,7 @@ func (c *statCollector) Update(ch chan<- prometheus.Metric) error { if temp, ok := cpuTemperatures[cpu]; ok { ch <- c.temp.mustNewConstMetric(temp, lcpu) } else { - level.Debug(c.logger).Log("msg", "no temperature information for CPU", "cpu", cpu) + c.logger.Debug("no temperature information for CPU", "cpu", cpu) ch <- c.temp.mustNewConstMetric(math.NaN(), lcpu) } } diff --git a/collector/cpu_openbsd.go b/collector/cpu_openbsd.go index 8715ff98..2d59f547 100644 --- a/collector/cpu_openbsd.go +++ b/collector/cpu_openbsd.go @@ -17,10 +17,10 @@ package collector import ( + "log/slog" "strconv" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -45,14 +45,14 @@ const ( type cpuCollector struct { cpu typedDesc - logger log.Logger + logger *slog.Logger } func init() { registerCollector("cpu", defaultEnabled, NewCPUCollector) } -func NewCPUCollector(logger log.Logger) (Collector, error) { +func NewCPUCollector(logger *slog.Logger) (Collector, error) { return &cpuCollector{ cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, logger: logger, diff --git a/collector/cpu_solaris.go b/collector/cpu_solaris.go index 32466a1e..550ba374 100644 --- a/collector/cpu_solaris.go +++ b/collector/cpu_solaris.go @@ -17,9 +17,9 @@ package collector import ( + "log/slog" "strconv" - "github.com/go-kit/log" "github.com/illumos/go-kstat" "github.com/prometheus/client_golang/prometheus" ) @@ -29,14 +29,14 @@ import "C" type cpuCollector struct { cpu typedDesc - logger log.Logger + logger *slog.Logger } func init() { registerCollector("cpu", defaultEnabled, NewCpuCollector) } -func NewCpuCollector(logger log.Logger) (Collector, error) { +func NewCpuCollector(logger *slog.Logger) (Collector, error) { return &cpuCollector{ cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, logger: logger, @@ -60,17 +60,17 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { } for k, v := range map[string]string{ - "idle": "cpu_ticks_idle", - "kernel": "cpu_ticks_kernel", - "user": "cpu_ticks_user", - "wait": "cpu_ticks_wait", + "idle": "cpu_nsec_idle", + "kernel": "cpu_nsec_kernel", + "user": "cpu_nsec_user", + "wait": "cpu_nsec_wait", } { kstatValue, err := ksCPU.GetNamed(v) if err != nil { return err } - ch <- c.cpu.mustNewConstMetric(float64(kstatValue.UintVal), strconv.Itoa(cpu), k) + ch <- c.cpu.mustNewConstMetric(float64(kstatValue.UintVal)/1e9, strconv.Itoa(cpu), k) } } return nil diff --git a/collector/cpu_vulnerabilities_linux.go b/collector/cpu_vulnerabilities_linux.go index 69a2c587..180d56d6 100644 --- a/collector/cpu_vulnerabilities_linux.go +++ b/collector/cpu_vulnerabilities_linux.go @@ -15,8 +15,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -40,7 +40,7 @@ func init() { registerCollector(cpuVulerabilitiesCollector, defaultDisabled, NewVulnerabilitySysfsCollector) } -func NewVulnerabilitySysfsCollector(logger log.Logger) (Collector, error) { +func NewVulnerabilitySysfsCollector(logger *slog.Logger) (Collector, error) { return &cpuVulnerabilitiesCollector{}, nil } diff --git a/collector/cpufreq_linux.go b/collector/cpufreq_linux.go index 3372be1a..d6b3e42d 100644 --- a/collector/cpufreq_linux.go +++ b/collector/cpufreq_linux.go @@ -18,15 +18,15 @@ package collector import ( "fmt" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" + "log/slog" "strings" ) type cpuFreqCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +34,7 @@ func init() { } // NewCPUFreqCollector returns a new Collector exposing kernel/system statistics. -func NewCPUFreqCollector(logger log.Logger) (Collector, error) { +func NewCPUFreqCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) diff --git a/collector/cpufreq_solaris.go b/collector/cpufreq_solaris.go index c3fb9ee6..c13703e1 100644 --- a/collector/cpufreq_solaris.go +++ b/collector/cpufreq_solaris.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "strconv" - "github.com/go-kit/log" "github.com/illumos/go-kstat" "github.com/prometheus/client_golang/prometheus" ) @@ -29,14 +29,14 @@ import ( import "C" type cpuFreqCollector struct { - logger log.Logger + logger *slog.Logger } func init() { registerCollector("cpufreq", defaultEnabled, NewCpuFreqCollector) } -func NewCpuFreqCollector(logger log.Logger) (Collector, error) { +func NewCpuFreqCollector(logger *slog.Logger) (Collector, error) { return &cpuFreqCollector{ logger: logger, }, nil diff --git a/collector/devstat_dragonfly.go b/collector/devstat_dragonfly.go index 11678054..b1796860 100644 --- a/collector/devstat_dragonfly.go +++ b/collector/devstat_dragonfly.go @@ -19,8 +19,8 @@ package collector import ( "errors" "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -98,7 +98,7 @@ type devstatCollector struct { bytesDesc *prometheus.Desc transfersDesc *prometheus.Desc blocksDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -106,7 +106,7 @@ func init() { } // NewDevstatCollector returns a new Collector exposing Device stats. -func NewDevstatCollector(logger log.Logger) (Collector, error) { +func NewDevstatCollector(logger *slog.Logger) (Collector, error) { return &devstatCollector{ bytesDesc: prometheus.NewDesc( prometheus.BuildFQName(namespace, devstatSubsystem, "bytes_total"), diff --git a/collector/devstat_freebsd.go b/collector/devstat_freebsd.go index 20cdc276..a9d42720 100644 --- a/collector/devstat_freebsd.go +++ b/collector/devstat_freebsd.go @@ -19,10 +19,10 @@ package collector import ( "errors" "fmt" + "log/slog" "sync" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -43,7 +43,7 @@ type devstatCollector struct { duration typedDesc busyTime typedDesc blocks typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -51,7 +51,7 @@ func init() { } // NewDevstatCollector returns a new Collector exposing Device stats. -func NewDevstatCollector(logger log.Logger) (Collector, error) { +func NewDevstatCollector(logger *slog.Logger) (Collector, error) { return &devstatCollector{ devinfo: &C.struct_devinfo{}, bytes: typedDesc{prometheus.NewDesc( diff --git a/collector/diskstats_aix.go b/collector/diskstats_aix.go new file mode 100644 index 00000000..c6619fd9 --- /dev/null +++ b/collector/diskstats_aix.go @@ -0,0 +1,82 @@ +// Copyright 2024 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 !nodiskstats +// +build !nodiskstats + +package collector + +import ( + "fmt" + "log/slog" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +const diskstatsDefaultIgnoredDevices = "" + +type diskstatsCollector struct { + rbytes typedDesc + wbytes typedDesc + time typedDesc + + deviceFilter deviceFilter + logger *slog.Logger + + tickPerSecond int64 +} + +func init() { + registerCollector("diskstats", defaultEnabled, NewDiskstatsCollector) +} + +// NewDiskstatsCollector returns a new Collector exposing disk device stats. +func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { + ticks, err := tickPerSecond() + if err != nil { + return nil, err + } + deviceFilter, err := newDiskstatsDeviceFilter(logger) + if err != nil { + return nil, fmt.Errorf("failed to parse device filter flags: %w", err) + } + + return &diskstatsCollector{ + rbytes: typedDesc{readBytesDesc, prometheus.CounterValue}, + wbytes: typedDesc{writtenBytesDesc, prometheus.CounterValue}, + time: typedDesc{ioTimeSecondsDesc, prometheus.CounterValue}, + + deviceFilter: deviceFilter, + logger: logger, + + tickPerSecond: ticks, + }, nil +} + +func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.DiskStat() + if err != nil { + return err + } + + for _, stat := range stats { + if c.deviceFilter.ignored(stat.Name) { + continue + } + 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) + } + return nil +} diff --git a/collector/diskstats_common.go b/collector/diskstats_common.go index 2ab84438..593d0558 100644 --- a/collector/diskstats_common.go +++ b/collector/diskstats_common.go @@ -11,18 +11,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodiskstats && (openbsd || linux || darwin) +//go:build !nodiskstats && (openbsd || linux || darwin || aix) // +build !nodiskstats -// +build openbsd linux darwin +// +build openbsd linux darwin aix package collector import ( "errors" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -93,10 +92,10 @@ var ( ) ) -func newDiskstatsDeviceFilter(logger log.Logger) (deviceFilter, error) { +func newDiskstatsDeviceFilter(logger *slog.Logger) (deviceFilter, error) { if *oldDiskstatsDeviceExclude != "" { if !diskstatsDeviceExcludeSet { - level.Warn(logger).Log("msg", "--collector.diskstats.ignored-devices is DEPRECATED and will be removed in 2.0.0, use --collector.diskstats.device-exclude") + logger.Warn("--collector.diskstats.ignored-devices is DEPRECATED and will be removed in 2.0.0, use --collector.diskstats.device-exclude") *diskstatsDeviceExclude = *oldDiskstatsDeviceExclude } else { return deviceFilter{}, errors.New("--collector.diskstats.ignored-devices and --collector.diskstats.device-exclude are mutually exclusive") @@ -108,11 +107,11 @@ func newDiskstatsDeviceFilter(logger log.Logger) (deviceFilter, error) { } if *diskstatsDeviceExclude != "" { - level.Info(logger).Log("msg", "Parsed flag --collector.diskstats.device-exclude", "flag", *diskstatsDeviceExclude) + logger.Info("Parsed flag --collector.diskstats.device-exclude", "flag", *diskstatsDeviceExclude) } if *diskstatsDeviceInclude != "" { - level.Info(logger).Log("msg", "Parsed Flag --collector.diskstats.device-include", "flag", *diskstatsDeviceInclude) + logger.Info("Parsed Flag --collector.diskstats.device-include", "flag", *diskstatsDeviceInclude) } return newDeviceFilter(*diskstatsDeviceExclude, *diskstatsDeviceInclude), nil diff --git a/collector/diskstats_darwin.go b/collector/diskstats_darwin.go index b5f6c538..98052fe7 100644 --- a/collector/diskstats_darwin.go +++ b/collector/diskstats_darwin.go @@ -18,8 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/lufia/iostat" "github.com/prometheus/client_golang/prometheus" ) @@ -35,7 +35,7 @@ type diskstatsCollector struct { descs []typedDescFunc deviceFilter deviceFilter - logger log.Logger + logger *slog.Logger } func init() { @@ -43,7 +43,7 @@ func init() { } // NewDiskstatsCollector returns a new Collector exposing disk device stats. -func NewDiskstatsCollector(logger log.Logger) (Collector, error) { +func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { var diskLabelNames = []string{"device"} deviceFilter, err := newDiskstatsDeviceFilter(logger) diff --git a/collector/diskstats_linux.go b/collector/diskstats_linux.go index ed5d044a..134c19e2 100644 --- a/collector/diskstats_linux.go +++ b/collector/diskstats_linux.go @@ -19,12 +19,11 @@ package collector import ( "bufio" "fmt" + "log/slog" "os" "strconv" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/blockdevice" ) @@ -85,7 +84,7 @@ type diskstatsCollector struct { filesystemInfoDesc typedFactorDesc deviceMapperInfoDesc typedFactorDesc ataDescs map[string]typedFactorDesc - logger log.Logger + logger *slog.Logger getUdevDeviceProperties func(uint32, uint32) (udevInfo, error) } @@ -95,7 +94,7 @@ func init() { // NewDiskstatsCollector returns a new Collector exposing disk device stats. // Docs from https://www.kernel.org/doc/Documentation/iostats.txt -func NewDiskstatsCollector(logger log.Logger) (Collector, error) { +func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { var diskLabelNames = []string{"device"} fs, err := blockdevice.NewFS(*procPath, *sysPath) if err != nil { @@ -262,7 +261,7 @@ func NewDiskstatsCollector(logger log.Logger) (Collector, error) { // Only enable getting device properties from udev if the directory is readable. if stat, err := os.Stat(*udevDataPath); err != nil || !stat.IsDir() { - level.Error(logger).Log("msg", "Failed to open directory, disabling udev device properties", "path", *udevDataPath) + logger.Error("Failed to open directory, disabling udev device properties", "path", *udevDataPath) } else { collector.getUdevDeviceProperties = getUdevDeviceProperties } @@ -284,7 +283,7 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { info, err := getUdevDeviceProperties(stats.MajorNumber, stats.MinorNumber) if err != nil { - level.Debug(c.logger).Log("msg", "Failed to parse udev info", "err", err) + c.logger.Debug("Failed to parse udev info", "err", err) } // This is usually the serial printed on the disk label. @@ -355,14 +354,14 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { for attr, desc := range c.ataDescs { str, ok := info[attr] if !ok { - level.Debug(c.logger).Log("msg", "Udev attribute does not exist", "attribute", attr) + c.logger.Debug("Udev attribute does not exist", "attribute", attr) continue } if value, err := strconv.ParseFloat(str, 64); err == nil { ch <- desc.mustNewConstMetric(value, dev) } else { - level.Error(c.logger).Log("msg", "Failed to parse ATA value", "err", err) + c.logger.Error("Failed to parse ATA value", "err", err) } } } diff --git a/collector/diskstats_linux_test.go b/collector/diskstats_linux_test.go index 8e969c3e..8c45d1c3 100644 --- a/collector/diskstats_linux_test.go +++ b/collector/diskstats_linux_test.go @@ -18,11 +18,11 @@ package collector import ( "fmt" - "os" + "io" + "log/slog" "strings" "testing" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" ) @@ -39,7 +39,7 @@ func (c testDiskStatsCollector) Describe(ch chan<- *prometheus.Desc) { prometheus.DescribeByCollect(c, ch) } -func NewTestDiskStatsCollector(logger log.Logger) (prometheus.Collector, error) { +func NewTestDiskStatsCollector(logger *slog.Logger) (prometheus.Collector, error) { dsc, err := NewDiskstatsCollector(logger) if err != nil { return testDiskStatsCollector{}, err @@ -317,10 +317,10 @@ node_disk_written_bytes_total{device="sr0"} 0 node_disk_written_bytes_total{device="vda"} 1.0938236928e+11 ` - logger := log.NewLogfmtLogger(os.Stderr) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) collector, err := NewDiskstatsCollector(logger) if err != nil { - panic(err) + t.Fatal(err) } c, err := NewTestDiskStatsCollector(logger) if err != nil { diff --git a/collector/diskstats_openbsd.go b/collector/diskstats_openbsd.go index 2a69042a..49415c17 100644 --- a/collector/diskstats_openbsd.go +++ b/collector/diskstats_openbsd.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -41,7 +41,7 @@ type diskstatsCollector struct { time typedDesc deviceFilter deviceFilter - logger log.Logger + logger *slog.Logger } func init() { @@ -49,7 +49,7 @@ func init() { } // NewDiskstatsCollector returns a new Collector exposing disk device stats. -func NewDiskstatsCollector(logger log.Logger) (Collector, error) { +func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { deviceFilter, err := newDiskstatsDeviceFilter(logger) if err != nil { return nil, fmt.Errorf("failed to parse device filter flags: %w", err) diff --git a/collector/diskstats_openbsd_amd64.go b/collector/diskstats_openbsd_amd64.go index a39cabb5..b290d889 100644 --- a/collector/diskstats_openbsd_amd64.go +++ b/collector/diskstats_openbsd_amd64.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -52,7 +52,7 @@ type diskstatsCollector struct { time typedDesc deviceFilter deviceFilter - logger log.Logger + logger *slog.Logger } func init() { @@ -60,7 +60,7 @@ func init() { } // NewDiskstatsCollector returns a new Collector exposing disk device stats. -func NewDiskstatsCollector(logger log.Logger) (Collector, error) { +func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { deviceFilter, err := newDiskstatsDeviceFilter(logger) if err != nil { return nil, fmt.Errorf("failed to parse device filter flags: %w", err) diff --git a/collector/dmi.go b/collector/dmi.go index 79475507..2282d7bd 100644 --- a/collector/dmi.go +++ b/collector/dmi.go @@ -19,11 +19,10 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -38,7 +37,7 @@ func init() { } // NewDMICollector returns a new Collector exposing DMI information. -func NewDMICollector(logger log.Logger) (Collector, error) { +func NewDMICollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -47,7 +46,7 @@ func NewDMICollector(logger log.Logger) (Collector, error) { dmi, err := fs.DMIClass() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(logger).Log("msg", "Platform does not support Desktop Management Interface (DMI) information", "err", err) + logger.Debug("Platform does not support Desktop Management Interface (DMI) information", "err", err) dmi = &sysfs.DMIClass{} } else { return nil, fmt.Errorf("failed to read Desktop Management Interface (DMI) information: %w", err) diff --git a/collector/drbd_linux.go b/collector/drbd_linux.go index f192a24c..cbaf8161 100644 --- a/collector/drbd_linux.go +++ b/collector/drbd_linux.go @@ -20,12 +20,11 @@ import ( "bufio" "errors" "fmt" + "log/slog" "os" "strconv" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -79,14 +78,14 @@ type drbdCollector struct { numerical map[string]drbdNumericalMetric stringPair map[string]drbdStringPairMetric connected *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { registerCollector("drbd", defaultDisabled, newDRBDCollector) } -func newDRBDCollector(logger log.Logger) (Collector, error) { +func newDRBDCollector(logger *slog.Logger) (Collector, error) { return &drbdCollector{ numerical: map[string]drbdNumericalMetric{ "ns": newDRBDNumericalMetric( @@ -191,7 +190,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { file, err := os.Open(statsFile) if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "stats file does not exist, skipping", "file", statsFile, "err", err) + c.logger.Debug("stats file does not exist, skipping", "file", statsFile, "err", err) return ErrNoData } @@ -208,7 +207,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { kv := strings.Split(field, ":") if len(kv) != 2 { - level.Debug(c.logger).Log("msg", "skipping invalid key:value pair", "field", field) + c.logger.Debug("skipping invalid key:value pair", "field", field) continue } @@ -274,7 +273,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { continue } - level.Debug(c.logger).Log("msg", "unhandled key-value pair", "key", kv[0], "value", kv[1]) + c.logger.Debug("unhandled key-value pair", "key", kv[0], "value", kv[1]) } return scanner.Err() diff --git a/collector/drm_linux.go b/collector/drm_linux.go index 11b8c628..61bb59ac 100644 --- a/collector/drm_linux.go +++ b/collector/drm_linux.go @@ -18,8 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -30,7 +30,7 @@ const ( type drmCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger CardInfo *prometheus.Desc GPUBusyPercent *prometheus.Desc MemoryGTTSize *prometheus.Desc @@ -46,7 +46,7 @@ func init() { } // NewDrmCollector returns a new Collector exposing /sys/class/drm/card?/device stats. -func NewDrmCollector(logger log.Logger) (Collector, error) { +func NewDrmCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) diff --git a/collector/edac_linux.go b/collector/edac_linux.go index c7719b7a..c14c96c3 100644 --- a/collector/edac_linux.go +++ b/collector/edac_linux.go @@ -18,10 +18,10 @@ package collector import ( "fmt" + "log/slog" "path/filepath" "regexp" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -39,7 +39,7 @@ type edacCollector struct { ueCount *prometheus.Desc csRowCECount *prometheus.Desc csRowUECount *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -47,7 +47,7 @@ func init() { } // NewEdacCollector returns a new Collector exposing edac stats. -func NewEdacCollector(logger log.Logger) (Collector, error) { +func NewEdacCollector(logger *slog.Logger) (Collector, error) { return &edacCollector{ ceCount: prometheus.NewDesc( prometheus.BuildFQName(namespace, edacSubsystem, "correctable_errors_total"), diff --git a/collector/entropy_linux.go b/collector/entropy_linux.go index 909d2ff4..6da61125 100644 --- a/collector/entropy_linux.go +++ b/collector/entropy_linux.go @@ -18,8 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -28,7 +28,7 @@ type entropyCollector struct { fs procfs.FS entropyAvail *prometheus.Desc entropyPoolSize *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -36,7 +36,7 @@ func init() { } // NewEntropyCollector returns a new Collector exposing entropy stats. -func NewEntropyCollector(logger log.Logger) (Collector, error) { +func NewEntropyCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/ethtool_linux.go b/collector/ethtool_linux.go index 7412c27c..da6be30b 100644 --- a/collector/ethtool_linux.go +++ b/collector/ethtool_linux.go @@ -23,6 +23,7 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "regexp" "sort" @@ -31,8 +32,6 @@ import ( "syscall" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" "github.com/safchain/ethtool" @@ -79,13 +78,13 @@ type ethtoolCollector struct { deviceFilter deviceFilter infoDesc *prometheus.Desc metricsPattern *regexp.Regexp - logger log.Logger + logger *slog.Logger } // makeEthtoolCollector is the internal constructor for EthtoolCollector. // This allows NewEthtoolTestCollector to override its .ethtool interface // for testing. -func makeEthtoolCollector(logger log.Logger) (*ethtoolCollector, error) { +func makeEthtoolCollector(logger *slog.Logger) (*ethtoolCollector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -96,6 +95,16 @@ func makeEthtoolCollector(logger log.Logger) (*ethtoolCollector, error) { return nil, fmt.Errorf("failed to initialize ethtool library: %w", err) } + if *ethtoolDeviceInclude != "" { + logger.Info("Parsed flag --collector.ethtool.device-include", "flag", *ethtoolDeviceInclude) + } + if *ethtoolDeviceExclude != "" { + logger.Info("Parsed flag --collector.ethtool.device-exclude", "flag", *ethtoolDeviceExclude) + } + if *ethtoolIncludedMetrics != "" { + logger.Info("Parsed flag --collector.ethtool.metrics-include", "flag", *ethtoolIncludedMetrics) + } + // Pre-populate some common ethtool metrics. return ðtoolCollector{ fs: fs, @@ -213,7 +222,7 @@ func buildEthtoolFQName(metric string) string { } // NewEthtoolCollector returns a new Collector exposing ethtool stats. -func NewEthtoolCollector(logger log.Logger) (Collector, error) { +func NewEthtoolCollector(logger *slog.Logger) (Collector, error) { return makeEthtoolCollector(logger) } @@ -366,7 +375,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { netClass, err := c.fs.NetClass() if err != nil { if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "Could not read netclass file", "err", err) + c.logger.Debug("Could not read netclass file", "err", err) return ErrNoData } return fmt.Errorf("could not get net class info: %w", err) @@ -395,12 +404,12 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { } else { if errno, ok := err.(syscall.Errno); ok { if err == unix.EOPNOTSUPP { - level.Debug(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Debug("ethtool link info error", "err", err, "device", device, "errno", uint(errno)) } else if errno != 0 { - level.Error(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Error("ethtool link info error", "err", err, "device", device, "errno", uint(errno)) } } else { - level.Error(c.logger).Log("msg", "ethtool link info error", "err", err, "device", device) + c.logger.Error("ethtool link info error", "err", err, "device", device) } } @@ -412,12 +421,12 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { } else { if errno, ok := err.(syscall.Errno); ok { if err == unix.EOPNOTSUPP { - level.Debug(c.logger).Log("msg", "ethtool driver info error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Debug("ethtool driver info error", "err", err, "device", device, "errno", uint(errno)) } else if errno != 0 { - level.Error(c.logger).Log("msg", "ethtool driver info error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Error("ethtool driver info error", "err", err, "device", device, "errno", uint(errno)) } } else { - level.Error(c.logger).Log("msg", "ethtool driver info error", "err", err, "device", device) + c.logger.Error("ethtool driver info error", "err", err, "device", device) } } @@ -428,12 +437,12 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { if errno, ok := err.(syscall.Errno); ok { if err == unix.EOPNOTSUPP { - level.Debug(c.logger).Log("msg", "ethtool stats error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Debug("ethtool stats error", "err", err, "device", device, "errno", uint(errno)) } else if errno != 0 { - level.Error(c.logger).Log("msg", "ethtool stats error", "err", err, "device", device, "errno", uint(errno)) + c.logger.Error("ethtool stats error", "err", err, "device", device, "errno", uint(errno)) } } else { - level.Error(c.logger).Log("msg", "ethtool stats error", "err", err, "device", device) + c.logger.Error("ethtool stats error", "err", err, "device", device) } } @@ -452,7 +461,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { metricFQName := buildEthtoolFQName(metricName) existingMetric, exists := metricFQNames[metricFQName] if exists { - level.Debug(c.logger).Log("msg", "dropping duplicate metric name", "device", device, + c.logger.Debug("dropping duplicate metric name", "device", device, "metricFQName", metricFQName, "metric1", existingMetric, "metric2", metricName) // Keep the metricName as "deleted" in the dict in case there are 3 duplicates. metricFQNames[metricFQName] = "" diff --git a/collector/ethtool_linux_test.go b/collector/ethtool_linux_test.go index cf55c6b1..98e66dbc 100644 --- a/collector/ethtool_linux_test.go +++ b/collector/ethtool_linux_test.go @@ -19,6 +19,8 @@ package collector import ( "bufio" "fmt" + "io" + "log/slog" "os" "path/filepath" "strconv" @@ -26,7 +28,6 @@ import ( "syscall" "testing" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/safchain/ethtool" @@ -49,7 +50,7 @@ func (c testEthtoolCollector) Describe(ch chan<- *prometheus.Desc) { prometheus.DescribeByCollect(c, ch) } -func NewTestEthtoolCollector(logger log.Logger) (prometheus.Collector, error) { +func NewTestEthtoolCollector(logger *slog.Logger) (prometheus.Collector, error) { dsc, err := NewEthtoolTestCollector(logger) if err != nil { return testEthtoolCollector{}, err @@ -255,14 +256,14 @@ func (e *EthtoolFixture) LinkInfo(intf string) (ethtool.EthtoolCmd, error) { return res, err } -func NewEthtoolTestCollector(logger log.Logger) (Collector, error) { +func NewEthtoolTestCollector(logger *slog.Logger) (Collector, error) { collector, err := makeEthtoolCollector(logger) - collector.ethtool = &EthtoolFixture{ - fixturePath: "fixtures/ethtool/", - } if err != nil { return nil, err } + collector.ethtool = &EthtoolFixture{ + fixturePath: "fixtures/ethtool/", + } return collector, nil } @@ -370,10 +371,10 @@ node_network_supported_speed_bytes{device="eth0",duplex="half",mode="10baseT"} 1 ` *sysPath = "fixtures/sys" - logger := log.NewLogfmtLogger(os.Stderr) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) collector, err := NewEthtoolTestCollector(logger) if err != nil { - panic(err) + t.Fatal(err) } c, err := NewTestEthtoolCollector(logger) if err != nil { diff --git a/collector/exec_bsd.go b/collector/exec_bsd.go index 87cb3cee..07de879e 100644 --- a/collector/exec_bsd.go +++ b/collector/exec_bsd.go @@ -18,13 +18,13 @@ package collector import ( - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) type execCollector struct { sysctls []bsdSysctl - logger log.Logger + logger *slog.Logger } func init() { @@ -32,7 +32,7 @@ func init() { } // NewExecCollector returns a new Collector exposing system execution statistics. -func NewExecCollector(logger log.Logger) (Collector, error) { +func NewExecCollector(logger *slog.Logger) (Collector, error) { // From sys/vm/vm_meter.c: // All are of type CTLTYPE_UINT. // diff --git a/collector/fibrechannel_linux.go b/collector/fibrechannel_linux.go index 32e4d1e7..cb8be9db 100644 --- a/collector/fibrechannel_linux.go +++ b/collector/fibrechannel_linux.go @@ -18,11 +18,11 @@ package collector import ( "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/node_exporter/collector/utils" "github.com/prometheus/procfs/sysfs" ) @@ -31,7 +31,7 @@ const maxUint64 = ^uint64(0) type fibrechannelCollector struct { fs sysfs.FS metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger subsystem string } @@ -40,7 +40,7 @@ func init() { } // NewFibreChannelCollector returns a new Collector exposing FibreChannel stats. -func NewFibreChannelCollector(logger log.Logger) (Collector, error) { +func NewFibreChannelCollector(logger *slog.Logger) (Collector, error) { var i fibrechannelCollector var err error @@ -66,18 +66,6 @@ func NewFibreChannelCollector(logger log.Logger) (Collector, error) { "rx_words_total": "Number of words received by host port", "tx_frames_total": "Number of frames transmitted by host port", "link_failure_total": "Number of times the host port link has failed", - "name": "Name of Fibre Channel HBA", - "speed": "Current operating speed", - "port_state": "Current port state", - "port_type": "Port type, what the port is connected to", - "symbolic_name": "Symbolic Name", - "node_name": "Node Name as hexadecimal string", - "port_id": "Port ID as string", - "port_name": "Port Name as hexadecimal string", - "fabric_name": "Fabric Name; 0 if PTP", - "dev_loss_tmo": "Device Loss Timeout in seconds", - "supported_classes": "The FC classes supported", - "supported_speeds": "The FC speeds supported", } i.metricDescs = make(map[string]*prometheus.Desc) @@ -110,7 +98,7 @@ func (c *fibrechannelCollector) Update(ch chan<- prometheus.Metric) error { hosts, err := c.fs.FibreChannelClass() if err != nil { if os.IsNotExist(err) { - level.Debug(c.logger).Log("msg", "fibrechannel statistics not found, skipping") + c.logger.Debug("fibrechannel statistics not found, skipping") return ErrNoData } return fmt.Errorf("error obtaining FibreChannel class info: %s", err) @@ -126,23 +114,36 @@ func (c *fibrechannelCollector) Update(ch chan<- prometheus.Metric) error { infoValue := 1.0 // First push the Host values - ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, host.Name, host.Speed, host.PortState, host.PortType, host.PortID, host.PortName, host.FabricName, host.SymbolicName, host.SupportedClasses, host.SupportedSpeeds, host.DevLossTMO) + ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, utils.SafeDereference( + host.Name, + host.Speed, + host.PortState, + host.PortType, + host.PortID, + host.PortName, + host.FabricName, + host.SymbolicName, + host.SupportedClasses, + host.SupportedSpeeds, + host.DevLossTMO, + )...) // Then the counters - c.pushCounter(ch, "dumped_frames_total", host.Counters.DumpedFrames, host.Name) - c.pushCounter(ch, "error_frames_total", host.Counters.ErrorFrames, host.Name) - c.pushCounter(ch, "invalid_crc_total", host.Counters.InvalidCRCCount, host.Name) - c.pushCounter(ch, "rx_frames_total", host.Counters.RXFrames, host.Name) - c.pushCounter(ch, "rx_words_total", host.Counters.RXWords, host.Name) - c.pushCounter(ch, "tx_frames_total", host.Counters.TXFrames, host.Name) - c.pushCounter(ch, "tx_words_total", host.Counters.TXWords, host.Name) - c.pushCounter(ch, "seconds_since_last_reset_total", host.Counters.SecondsSinceLastReset, host.Name) - c.pushCounter(ch, "invalid_tx_words_total", host.Counters.InvalidTXWordCount, host.Name) - c.pushCounter(ch, "link_failure_total", host.Counters.LinkFailureCount, host.Name) - c.pushCounter(ch, "loss_of_sync_total", host.Counters.LossOfSyncCount, host.Name) - c.pushCounter(ch, "loss_of_signal_total", host.Counters.LossOfSignalCount, host.Name) - c.pushCounter(ch, "nos_total", host.Counters.NosCount, host.Name) - c.pushCounter(ch, "fcp_packet_aborts_total", host.Counters.FCPPacketAborts, host.Name) + // Note: `procfs` guarantees these a safe dereference for these counters. + c.pushCounter(ch, "dumped_frames_total", *host.Counters.DumpedFrames, *host.Name) + c.pushCounter(ch, "error_frames_total", *host.Counters.ErrorFrames, *host.Name) + c.pushCounter(ch, "invalid_crc_total", *host.Counters.InvalidCRCCount, *host.Name) + c.pushCounter(ch, "rx_frames_total", *host.Counters.RXFrames, *host.Name) + c.pushCounter(ch, "rx_words_total", *host.Counters.RXWords, *host.Name) + c.pushCounter(ch, "tx_frames_total", *host.Counters.TXFrames, *host.Name) + c.pushCounter(ch, "tx_words_total", *host.Counters.TXWords, *host.Name) + c.pushCounter(ch, "seconds_since_last_reset_total", *host.Counters.SecondsSinceLastReset, *host.Name) + c.pushCounter(ch, "invalid_tx_words_total", *host.Counters.InvalidTXWordCount, *host.Name) + c.pushCounter(ch, "link_failure_total", *host.Counters.LinkFailureCount, *host.Name) + c.pushCounter(ch, "loss_of_sync_total", *host.Counters.LossOfSyncCount, *host.Name) + c.pushCounter(ch, "loss_of_signal_total", *host.Counters.LossOfSignalCount, *host.Name) + c.pushCounter(ch, "nos_total", *host.Counters.NosCount, *host.Name) + c.pushCounter(ch, "fcp_packet_aborts_total", *host.Counters.FCPPacketAborts, *host.Name) } return nil diff --git a/collector/filefd_linux.go b/collector/filefd_linux.go index 55069d6c..39a72fcd 100644 --- a/collector/filefd_linux.go +++ b/collector/filefd_linux.go @@ -20,10 +20,10 @@ import ( "bytes" "fmt" "io" + "log/slog" "os" "strconv" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -32,7 +32,7 @@ const ( ) type fileFDStatCollector struct { - logger log.Logger + logger *slog.Logger } func init() { @@ -40,7 +40,7 @@ func init() { } // NewFileFDStatCollector returns a new Collector exposing file-nr stats. -func NewFileFDStatCollector(logger log.Logger) (Collector, error) { +func NewFileFDStatCollector(logger *slog.Logger) (Collector, error) { return &fileFDStatCollector{logger}, nil } diff --git a/collector/filesystem_aix.go b/collector/filesystem_aix.go new file mode 100644 index 00000000..863a26a0 --- /dev/null +++ b/collector/filesystem_aix.go @@ -0,0 +1,65 @@ +// Copyright 2024 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 !nofilesystem +// +build !nofilesystem + +package collector + +import ( + "github.com/power-devops/perfstat" +) + +const ( + defMountPointsExcluded = "^/(dev|aha)($|/)" + defFSTypesExcluded = "^procfs$" +) + +// Expose filesystem fullness. +func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { + fsStat, err := perfstat.FileSystemStat() + if err != nil { + return nil, err + } + for _, stat := range fsStat { + if c.excludedMountPointsPattern.MatchString(stat.MountPoint) { + c.logger.Debug("Ignoring mount point", "mountpoint", stat.MountPoint) + continue + } + fstype := stat.TypeString() + if c.excludedFSTypesPattern.MatchString(fstype) { + c.logger.Debug("Ignoring fs type", "type", fstype) + continue + } + + ro := 0.0 + if stat.Flags&perfstat.VFS_READONLY != 0 { + ro = 1.0 + } + + stats = append(stats, filesystemStats{ + labels: filesystemLabels{ + device: stat.Device, + 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. + files: float64(stat.TotalInodes), + filesFree: float64(stat.FreeInodes), + ro: ro, + }) + } + return stats, nil +} diff --git a/collector/filesystem_bsd.go b/collector/filesystem_bsd.go index d3025a01..2db3fb8f 100644 --- a/collector/filesystem_bsd.go +++ b/collector/filesystem_bsd.go @@ -20,8 +20,6 @@ package collector import ( "errors" "unsafe" - - "github.com/go-kit/log/level" ) /* @@ -51,14 +49,14 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { for i := 0; i < int(count); i++ { mountpoint := C.GoString(&mnt[i].f_mntonname[0]) if c.excludedMountPointsPattern.MatchString(mountpoint) { - level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", mountpoint) + c.logger.Debug("Ignoring mount point", "mountpoint", mountpoint) continue } device := C.GoString(&mnt[i].f_mntfromname[0]) fstype := C.GoString(&mnt[i].f_fstypename[0]) if c.excludedFSTypesPattern.MatchString(fstype) { - level.Debug(c.logger).Log("msg", "Ignoring fs type", "type", fstype) + c.logger.Debug("Ignoring fs type", "type", fstype) continue } diff --git a/collector/filesystem_common.go b/collector/filesystem_common.go index f5d51352..0ec55e8a 100644 --- a/collector/filesystem_common.go +++ b/collector/filesystem_common.go @@ -11,19 +11,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nofilesystem && (linux || freebsd || openbsd || darwin || dragonfly) +//go:build !nofilesystem && (linux || freebsd || netbsd || openbsd || darwin || dragonfly || aix) // +build !nofilesystem -// +build linux freebsd openbsd darwin dragonfly +// +build linux freebsd netbsd openbsd darwin dragonfly aix package collector import ( "errors" + "log/slog" "regexp" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -69,11 +68,12 @@ type filesystemCollector struct { sizeDesc, freeDesc, availDesc *prometheus.Desc filesDesc, filesFreeDesc *prometheus.Desc roDesc, deviceErrorDesc *prometheus.Desc - logger log.Logger + mountInfoDesc *prometheus.Desc + logger *slog.Logger } type filesystemLabels struct { - device, mountPoint, fsType, options, deviceError string + device, mountPoint, fsType, options, deviceError, major, minor string } type filesystemStats struct { @@ -88,10 +88,10 @@ func init() { } // NewFilesystemCollector returns a new Collector exposing filesystems stats. -func NewFilesystemCollector(logger log.Logger) (Collector, error) { +func NewFilesystemCollector(logger *slog.Logger) (Collector, error) { if *oldMountPointsExcluded != "" { if !mountPointsExcludeSet { - level.Warn(logger).Log("msg", "--collector.filesystem.ignored-mount-points is DEPRECATED and will be removed in 2.0.0, use --collector.filesystem.mount-points-exclude") + logger.Warn("--collector.filesystem.ignored-mount-points is DEPRECATED and will be removed in 2.0.0, use --collector.filesystem.mount-points-exclude") *mountPointsExclude = *oldMountPointsExcluded } else { return nil, errors.New("--collector.filesystem.ignored-mount-points and --collector.filesystem.mount-points-exclude are mutually exclusive") @@ -100,7 +100,7 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { if *oldFSTypesExcluded != "" { if !fsTypesExcludeSet { - level.Warn(logger).Log("msg", "--collector.filesystem.ignored-fs-types is DEPRECATED and will be removed in 2.0.0, use --collector.filesystem.fs-types-exclude") + logger.Warn("--collector.filesystem.ignored-fs-types is DEPRECATED and will be removed in 2.0.0, use --collector.filesystem.fs-types-exclude") *fsTypesExclude = *oldFSTypesExcluded } else { return nil, errors.New("--collector.filesystem.ignored-fs-types and --collector.filesystem.fs-types-exclude are mutually exclusive") @@ -108,9 +108,9 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { } subsystem := "filesystem" - level.Info(logger).Log("msg", "Parsed flag --collector.filesystem.mount-points-exclude", "flag", *mountPointsExclude) + logger.Info("Parsed flag --collector.filesystem.mount-points-exclude", "flag", *mountPointsExclude) mountPointPattern := regexp.MustCompile(*mountPointsExclude) - level.Info(logger).Log("msg", "Parsed flag --collector.filesystem.fs-types-exclude", "flag", *fsTypesExclude) + logger.Info("Parsed flag --collector.filesystem.fs-types-exclude", "flag", *fsTypesExclude) filesystemsTypesPattern := regexp.MustCompile(*fsTypesExclude) sizeDesc := prometheus.NewDesc( @@ -155,6 +155,13 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { filesystemLabelNames, nil, ) + mountInfoDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "mount_info"), + "Filesystem mount information.", + []string{"device", "major", "minor", "mountpoint"}, + nil, + ) + return &filesystemCollector{ excludedMountPointsPattern: mountPointPattern, excludedFSTypesPattern: filesystemsTypesPattern, @@ -165,6 +172,7 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { filesFreeDesc: filesFreeDesc, roDesc: roDesc, deviceErrorDesc: deviceErrorDesc, + mountInfoDesc: mountInfoDesc, logger: logger, }, nil } @@ -215,6 +223,10 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error { c.filesFreeDesc, prometheus.GaugeValue, s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError, ) + ch <- prometheus.MustNewConstMetric( + c.mountInfoDesc, prometheus.GaugeValue, + 1.0, s.labels.device, s.labels.major, s.labels.minor, s.labels.mountPoint, + ) } return nil } diff --git a/collector/filesystem_freebsd.go b/collector/filesystem_freebsd.go index f05702d1..2a6026d3 100644 --- a/collector/filesystem_freebsd.go +++ b/collector/filesystem_freebsd.go @@ -17,7 +17,6 @@ package collector import ( - "github.com/go-kit/log/level" "golang.org/x/sys/unix" ) @@ -41,19 +40,19 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { for _, fs := range buf { mountpoint := unix.ByteSliceToString(fs.Mntonname[:]) if c.excludedMountPointsPattern.MatchString(mountpoint) { - level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", mountpoint) + c.logger.Debug("Ignoring mount point", "mountpoint", mountpoint) continue } device := unix.ByteSliceToString(fs.Mntfromname[:]) fstype := unix.ByteSliceToString(fs.Fstypename[:]) if c.excludedFSTypesPattern.MatchString(fstype) { - level.Debug(c.logger).Log("msg", "Ignoring fs type", "type", fstype) + c.logger.Debug("Ignoring fs type", "type", fstype) continue } if (fs.Flags & unix.MNT_IGNORE) != 0 { - level.Debug(c.logger).Log("msg", "Ignoring mount flagged as ignore", "mountpoint", mountpoint) + c.logger.Debug("Ignoring mount flagged as ignore", "mountpoint", mountpoint) continue } diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index 1d0c8493..19c39717 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -21,14 +21,13 @@ import ( "errors" "fmt" "io" + "log/slog" "os" "strings" "sync" "time" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "golang.org/x/sys/unix" ) @@ -75,11 +74,11 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { go func() { for _, labels := range mps { if c.excludedMountPointsPattern.MatchString(labels.mountPoint) { - level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", labels.mountPoint) + c.logger.Debug("Ignoring mount point", "mountpoint", labels.mountPoint) continue } if c.excludedFSTypesPattern.MatchString(labels.fsType) { - level.Debug(c.logger).Log("msg", "Ignoring fs", "type", labels.fsType) + c.logger.Debug("Ignoring fs", "type", labels.fsType) continue } @@ -90,7 +89,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { labels: labels, deviceError: 1, }) - level.Debug(c.logger).Log("msg", "Mount point is in an unresponsive state", "mountpoint", labels.mountPoint) + c.logger.Debug("Mount point is in an unresponsive state", "mountpoint", labels.mountPoint) stuckMountsMtx.Unlock() continue } @@ -128,14 +127,14 @@ func (c *filesystemCollector) processStat(labels filesystemLabels) filesystemSta // If the mount has been marked as stuck, unmark it and log it's recovery. if _, ok := stuckMounts[labels.mountPoint]; ok { - level.Debug(c.logger).Log("msg", "Mount point has recovered, monitoring will resume", "mountpoint", labels.mountPoint) + c.logger.Debug("Mount point has recovered, monitoring will resume", "mountpoint", labels.mountPoint) delete(stuckMounts, labels.mountPoint) } stuckMountsMtx.Unlock() if err != nil { labels.deviceError = err.Error() - level.Debug(c.logger).Log("msg", "Error on statfs() system call", "rootfs", rootfsFilePath(labels.mountPoint), "err", err) + c.logger.Debug("Error on statfs() system call", "rootfs", rootfsFilePath(labels.mountPoint), "err", err) return filesystemStats{ labels: labels, deviceError: 1, @@ -157,7 +156,7 @@ func (c *filesystemCollector) processStat(labels filesystemLabels) filesystemSta // stuckMountWatcher listens on the given success channel and if the channel closes // then the watcher does nothing. If instead the timeout is reached, the // mount point that is being watched is marked as stuck. -func stuckMountWatcher(mountPoint string, success chan struct{}, logger log.Logger) { +func stuckMountWatcher(mountPoint string, success chan struct{}, logger *slog.Logger) { mountCheckTimer := time.NewTimer(*mountTimeout) defer mountCheckTimer.Stop() select { @@ -170,19 +169,19 @@ func stuckMountWatcher(mountPoint string, success chan struct{}, logger log.Logg case <-success: // Success came in just after the timeout was reached, don't label the mount as stuck default: - level.Debug(logger).Log("msg", "Mount point timed out, it is being labeled as stuck and will not be monitored", "mountpoint", mountPoint) + logger.Debug("Mount point timed out, it is being labeled as stuck and will not be monitored", "mountpoint", mountPoint) stuckMounts[mountPoint] = struct{}{} } stuckMountsMtx.Unlock() } } -func mountPointDetails(logger log.Logger) ([]filesystemLabels, error) { - file, err := os.Open(procFilePath("1/mounts")) +func mountPointDetails(logger *slog.Logger) ([]filesystemLabels, error) { + file, err := os.Open(procFilePath("1/mountinfo")) if errors.Is(err, os.ErrNotExist) { - // Fallback to `/proc/mounts` if `/proc/1/mounts` is missing due hidepid. - level.Debug(logger).Log("msg", "Reading root mounts failed, falling back to system mounts", "err", err) - file, err = os.Open(procFilePath("mounts")) + // Fallback to `/proc/self/mountinfo` if `/proc/1/mountinfo` is missing due hidepid. + logger.Debug("Reading root mounts failed, falling back to self mounts", "err", err) + file, err = os.Open(procFilePath("self/mountinfo")) } if err != nil { return nil, err @@ -199,20 +198,33 @@ func parseFilesystemLabels(r io.Reader) ([]filesystemLabels, error) { for scanner.Scan() { parts := strings.Fields(scanner.Text()) - if len(parts) < 4 { + if len(parts) < 10 { return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text()) } + major, minor := 0, 0 + _, err := fmt.Sscanf(parts[2], "%d:%d", &major, &minor) + if err != nil { + return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text()) + } + + m := 5 + for parts[m+1] != "-" { + m++ + } + // Ensure we handle the translation of \040 and \011 // as per fstab(5). - parts[1] = strings.Replace(parts[1], "\\040", " ", -1) - parts[1] = strings.Replace(parts[1], "\\011", "\t", -1) + parts[4] = strings.Replace(parts[4], "\\040", " ", -1) + parts[4] = strings.Replace(parts[4], "\\011", "\t", -1) filesystems = append(filesystems, filesystemLabels{ - device: parts[0], - mountPoint: rootfsStripPrefix(parts[1]), - fsType: parts[2], - options: parts[3], + device: parts[m+3], + mountPoint: rootfsStripPrefix(parts[4]), + fsType: parts[m+2], + options: parts[5], + major: fmt.Sprint(major), + minor: fmt.Sprint(minor), deviceError: "", }) } diff --git a/collector/filesystem_linux_test.go b/collector/filesystem_linux_test.go index 325ffc87..d088598f 100644 --- a/collector/filesystem_linux_test.go +++ b/collector/filesystem_linux_test.go @@ -17,11 +17,12 @@ package collector import ( + "io" + "log/slog" "strings" "testing" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" ) func Test_parseFilesystemLabelsError(t *testing.T) { @@ -82,15 +83,23 @@ func TestMountPointDetails(t *testing.T) { "/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore] bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk": "", } - filesystems, err := mountPointDetails(log.NewNopLogger()) + filesystems, err := mountPointDetails(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Log(err) } + foundSet := map[string]bool{} for _, fs := range filesystems { if _, ok := expected[fs.mountPoint]; !ok { t.Errorf("Got unexpected %s", fs.mountPoint) } + foundSet[fs.mountPoint] = true + } + + for mountPoint := range expected { + if _, ok := foundSet[mountPoint]; !ok { + t.Errorf("Expected %s, got nothing", mountPoint) + } } } @@ -103,7 +112,7 @@ func TestMountsFallback(t *testing.T) { "/": "", } - filesystems, err := mountPointDetails(log.NewNopLogger()) + filesystems, err := mountPointDetails(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Log(err) } @@ -131,7 +140,7 @@ func TestPathRootfs(t *testing.T) { "/sys/fs/cgroup": "", } - filesystems, err := mountPointDetails(log.NewNopLogger()) + filesystems, err := mountPointDetails(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Log(err) } diff --git a/collector/filesystem_netbsd.go b/collector/filesystem_netbsd.go new file mode 100644 index 00000000..ba6a9f55 --- /dev/null +++ b/collector/filesystem_netbsd.go @@ -0,0 +1,132 @@ +// Copyright 2024 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 !nofilesystem +// +build !nofilesystem + +package collector + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + defMountPointsExcluded = "^/(dev)($|/)" + defFSTypesExcluded = "^(kernfs|procfs|ptyfs|fdesc)$" + _VFS_NAMELEN = 32 + _VFS_MNAMELEN = 1024 +) + +/* + * Go uses the NetBSD 9 ABI and thus syscall.SYS_GETVFSSTAT is compat_90_getvfsstat. + * We have to declare struct statvfs90 because it is not included in the unix package. + * See NetBSD/src/sys/compat/sys/statvfs.h. + */ +type statvfs90 struct { + F_flag uint + F_bsize uint + F_frsize uint + F_iosize uint + + F_blocks uint64 + F_bfree uint64 + F_bavail uint64 + F_bresvd uint64 + + F_files uint64 + F_ffree uint64 + F_favail uint64 + F_fresvd uint64 + + F_syncreads uint64 + F_syncwrites uint64 + + F_asyncreads uint64 + F_asyncwrites uint64 + + F_fsidx [2]uint32 + F_fsid uint32 + F_namemax uint + F_owner uint32 + F_spare [4]uint32 + + F_fstypename [_VFS_NAMELEN]byte + F_mntonname [_VFS_MNAMELEN]byte + F_mntfromname [_VFS_MNAMELEN]byte + + cgo_pad [4]byte +} + +func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { + var mnt []statvfs90 + if syscall.SYS_GETVFSSTAT != 356 /* compat_90_getvfsstat */ { + /* + * Catch if golang ever updates to newer ABI and bail. + */ + return nil, fmt.Errorf("getvfsstat: ABI mismatch") + } + for { + r1, _, errno := syscall.Syscall(syscall.SYS_GETVFSSTAT, uintptr(0), 0, unix.ST_NOWAIT) + if errno != 0 { + return nil, fmt.Errorf("getvfsstat: %s", string(errno)) + } + mnt = make([]statvfs90, r1, r1) + r2, _, errno := syscall.Syscall(syscall.SYS_GETVFSSTAT, uintptr(unsafe.Pointer(&mnt[0])), unsafe.Sizeof(mnt[0])*r1, unix.ST_NOWAIT /* ST_NOWAIT */) + if errno != 0 { + return nil, fmt.Errorf("getvfsstat: %s", string(errno)) + } + if r1 == r2 { + break + } + } + + stats = []filesystemStats{} + for _, v := range mnt { + mountpoint := unix.ByteSliceToString(v.F_mntonname[:]) + if c.excludedMountPointsPattern.MatchString(mountpoint) { + c.logger.Debug("msg", "Ignoring mount point", "mountpoint", mountpoint) + continue + } + + device := unix.ByteSliceToString(v.F_mntfromname[:]) + fstype := unix.ByteSliceToString(v.F_fstypename[:]) + if c.excludedFSTypesPattern.MatchString(fstype) { + c.logger.Debug("msg", "Ignoring fs type", "type", fstype) + continue + } + + var ro float64 + if (v.F_flag & unix.MNT_RDONLY) != 0 { + ro = 1 + } + + stats = append(stats, filesystemStats{ + labels: filesystemLabels{ + device: device, + mountPoint: mountpoint, + fsType: fstype, + }, + size: float64(v.F_blocks) * float64(v.F_bsize), + free: float64(v.F_bfree) * float64(v.F_bsize), + avail: float64(v.F_bavail) * float64(v.F_bsize), + files: float64(v.F_files), + filesFree: float64(v.F_ffree), + ro: ro, + }) + } + return stats, nil +} diff --git a/collector/filesystem_openbsd.go b/collector/filesystem_openbsd.go index 1c1e479e..d7489364 100644 --- a/collector/filesystem_openbsd.go +++ b/collector/filesystem_openbsd.go @@ -17,7 +17,6 @@ package collector import ( - "github.com/go-kit/log/level" "golang.org/x/sys/unix" ) @@ -43,14 +42,14 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { for _, v := range mnt { mountpoint := unix.ByteSliceToString(v.F_mntonname[:]) if c.excludedMountPointsPattern.MatchString(mountpoint) { - level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", mountpoint) + c.logger.Debug("Ignoring mount point", "mountpoint", mountpoint) continue } device := unix.ByteSliceToString(v.F_mntfromname[:]) fstype := unix.ByteSliceToString(v.F_fstypename[:]) if c.excludedFSTypesPattern.MatchString(fstype) { - level.Debug(c.logger).Log("msg", "Ignoring fs type", "type", fstype) + c.logger.Debug("Ignoring fs type", "type", fstype) continue } diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index e3de810c..605149da 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -1,55 +1,59 @@ -# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles. # TYPE go_gc_duration_seconds summary +# HELP go_gc_gogc_percent Heap size target percentage configured by the user, otherwise 100. This value is set by the GOGC environment variable, and the runtime/debug.SetGCPercent function. Sourced from /gc/gogc:percent +# TYPE go_gc_gogc_percent gauge +# HELP go_gc_gomemlimit_bytes Go runtime memory limit configured by the user, otherwise math.MaxInt64. This value is set by the GOMEMLIMIT environment variable, and the runtime/debug.SetMemoryLimit function. Sourced from /gc/gomemlimit:bytes +# TYPE go_gc_gomemlimit_bytes gauge # HELP go_goroutines Number of goroutines that currently exist. # TYPE go_goroutines gauge # HELP go_info Information about the Go environment. # TYPE go_info gauge -# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# HELP go_memstats_alloc_bytes Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes. # TYPE go_memstats_alloc_bytes gauge -# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes. # TYPE go_memstats_alloc_bytes_total counter -# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes. # TYPE go_memstats_buck_hash_sys_bytes gauge -# HELP go_memstats_frees_total Total number of frees. +# HELP go_memstats_frees_total Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects. # TYPE go_memstats_frees_total counter -# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes. # TYPE go_memstats_gc_sys_bytes gauge -# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. +# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes. # TYPE go_memstats_heap_alloc_bytes gauge -# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. +# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes. # TYPE go_memstats_heap_idle_bytes gauge -# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. +# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes # TYPE go_memstats_heap_inuse_bytes gauge -# HELP go_memstats_heap_objects Number of allocated objects. +# HELP go_memstats_heap_objects Number of currently allocated objects. Equals to /gc/heap/objects:objects. # TYPE go_memstats_heap_objects gauge -# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. +# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes. # TYPE go_memstats_heap_released_bytes gauge -# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. +# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes. # TYPE go_memstats_heap_sys_bytes gauge # HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. # TYPE go_memstats_last_gc_time_seconds gauge -# HELP go_memstats_lookups_total Total number of pointer lookups. -# TYPE go_memstats_lookups_total counter -# HELP go_memstats_mallocs_total Total number of mallocs. +# HELP go_memstats_mallocs_total Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects. # TYPE go_memstats_mallocs_total counter -# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. +# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes. # TYPE go_memstats_mcache_inuse_bytes gauge -# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. +# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes. # TYPE go_memstats_mcache_sys_bytes gauge -# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. +# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes. # TYPE go_memstats_mspan_inuse_bytes gauge -# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. +# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes. # TYPE go_memstats_mspan_sys_bytes gauge -# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. +# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes. # TYPE go_memstats_next_gc_bytes gauge -# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. +# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes. # TYPE go_memstats_other_sys_bytes gauge -# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. +# HELP go_memstats_stack_inuse_bytes Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes. # TYPE go_memstats_stack_inuse_bytes gauge -# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. +# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes. # TYPE go_memstats_stack_sys_bytes gauge -# HELP go_memstats_sys_bytes Number of bytes obtained from system. +# HELP go_memstats_sys_bytes Number of bytes obtained from system. Equals to /memory/classes/total:byte. # TYPE go_memstats_sys_bytes gauge +# HELP go_sched_gomaxprocs_threads The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously. Sourced from /sched/gomaxprocs:threads +# TYPE go_sched_gomaxprocs_threads gauge # HELP go_threads Number of OS threads created. # TYPE go_threads gauge # HELP node_arp_entries ARP entries by device @@ -161,6 +165,14 @@ node_btrfs_allocation_ratio{block_group_type="metadata",mode="raid1",uuid="0abb2 node_btrfs_allocation_ratio{block_group_type="metadata",mode="raid6",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 2 node_btrfs_allocation_ratio{block_group_type="system",mode="raid1",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 2 node_btrfs_allocation_ratio{block_group_type="system",mode="raid6",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 2 +# HELP node_btrfs_commit_seconds_total Sum of the duration of all commits, in seconds. +# TYPE node_btrfs_commit_seconds_total counter +node_btrfs_commit_seconds_total{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 47836.09 +node_btrfs_commit_seconds_total{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 +# HELP node_btrfs_commits_total The total number of commits that have occurred. +# TYPE node_btrfs_commits_total counter +node_btrfs_commits_total{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 258051 +node_btrfs_commits_total{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 # HELP node_btrfs_device_size_bytes Size of a device that is part of the filesystem. # TYPE node_btrfs_device_size_bytes gauge node_btrfs_device_size_bytes{device="loop22",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1.073741824e+10 @@ -177,6 +189,14 @@ node_btrfs_global_rsv_size_bytes{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1. # TYPE node_btrfs_info gauge node_btrfs_info{label="",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1 node_btrfs_info{label="fixture",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 1 +# HELP node_btrfs_last_commit_seconds Duration of the most recent commit, in seconds. +# TYPE node_btrfs_last_commit_seconds gauge +node_btrfs_last_commit_seconds{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 1 +node_btrfs_last_commit_seconds{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 +# HELP node_btrfs_max_commit_seconds Duration of the slowest commit, in seconds. +# TYPE node_btrfs_max_commit_seconds gauge +node_btrfs_max_commit_seconds{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 51.462 +node_btrfs_max_commit_seconds{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 # HELP node_btrfs_reserved_bytes Amount of space reserved for a data type # TYPE node_btrfs_reserved_bytes gauge node_btrfs_reserved_bytes{block_group_type="data",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 0 @@ -742,48 +762,64 @@ node_entropy_available_bits 1337 node_entropy_pool_size_bits 4096 # HELP node_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build. # TYPE node_exporter_build_info gauge +# HELP node_fibrechannel_dumped_frames_total Number of dumped frames +# TYPE node_fibrechannel_dumped_frames_total counter +node_fibrechannel_dumped_frames_total{fc_host="host1"} 0 # HELP node_fibrechannel_error_frames_total Number of errors in frames # TYPE node_fibrechannel_error_frames_total counter node_fibrechannel_error_frames_total{fc_host="host0"} 0 +node_fibrechannel_error_frames_total{fc_host="host1"} 19 # HELP node_fibrechannel_fcp_packet_aborts_total Number of aborted packets # TYPE node_fibrechannel_fcp_packet_aborts_total counter node_fibrechannel_fcp_packet_aborts_total{fc_host="host0"} 19 # HELP node_fibrechannel_info Non-numeric data from /sys/class/fc_host/, value is always 1. # TYPE node_fibrechannel_info gauge +node_fibrechannel_info{dev_loss_tmo="",fabric_name="",fc_host="host1",port_id="",port_name="",port_state="",port_type="",speed="8 Gbit",supported_classes="",supported_speeds="",symbolic_name=""} 1 node_fibrechannel_info{dev_loss_tmo="30",fabric_name="0",fc_host="host0",port_id="000002",port_name="1000e0071bce95f2",port_state="Online",port_type="Point-To-Point (direct nport connection)",speed="16 Gbit",supported_classes="Class 3",supported_speeds="4 Gbit, 8 Gbit, 16 Gbit",symbolic_name="Emulex SN1100E2P FV12.4.270.3 DV12.4.0.0. HN:gotest. OS:Linux"} 1 # HELP node_fibrechannel_invalid_crc_total Invalid Cyclic Redundancy Check count # TYPE node_fibrechannel_invalid_crc_total counter node_fibrechannel_invalid_crc_total{fc_host="host0"} 2 +node_fibrechannel_invalid_crc_total{fc_host="host1"} 32 # HELP node_fibrechannel_invalid_tx_words_total Number of invalid words transmitted by host port # TYPE node_fibrechannel_invalid_tx_words_total counter node_fibrechannel_invalid_tx_words_total{fc_host="host0"} 8 +node_fibrechannel_invalid_tx_words_total{fc_host="host1"} 128 # HELP node_fibrechannel_link_failure_total Number of times the host port link has failed # TYPE node_fibrechannel_link_failure_total counter node_fibrechannel_link_failure_total{fc_host="host0"} 9 +node_fibrechannel_link_failure_total{fc_host="host1"} 144 # HELP node_fibrechannel_loss_of_signal_total Number of times signal has been lost # TYPE node_fibrechannel_loss_of_signal_total counter node_fibrechannel_loss_of_signal_total{fc_host="host0"} 17 +node_fibrechannel_loss_of_signal_total{fc_host="host1"} 272 # HELP node_fibrechannel_loss_of_sync_total Number of failures on either bit or transmission word boundaries # TYPE node_fibrechannel_loss_of_sync_total counter node_fibrechannel_loss_of_sync_total{fc_host="host0"} 16 +node_fibrechannel_loss_of_sync_total{fc_host="host1"} 256 # HELP node_fibrechannel_nos_total Number Not_Operational Primitive Sequence received by host port # TYPE node_fibrechannel_nos_total counter node_fibrechannel_nos_total{fc_host="host0"} 18 +node_fibrechannel_nos_total{fc_host="host1"} 288 # HELP node_fibrechannel_rx_frames_total Number of frames received # TYPE node_fibrechannel_rx_frames_total counter node_fibrechannel_rx_frames_total{fc_host="host0"} 3 +node_fibrechannel_rx_frames_total{fc_host="host1"} 48 # HELP node_fibrechannel_rx_words_total Number of words received by host port # TYPE node_fibrechannel_rx_words_total counter node_fibrechannel_rx_words_total{fc_host="host0"} 4 +node_fibrechannel_rx_words_total{fc_host="host1"} 64 # HELP node_fibrechannel_seconds_since_last_reset_total Number of seconds since last host port reset # TYPE node_fibrechannel_seconds_since_last_reset_total counter node_fibrechannel_seconds_since_last_reset_total{fc_host="host0"} 7 +node_fibrechannel_seconds_since_last_reset_total{fc_host="host1"} 112 # HELP node_fibrechannel_tx_frames_total Number of frames transmitted by host port # TYPE node_fibrechannel_tx_frames_total counter node_fibrechannel_tx_frames_total{fc_host="host0"} 5 +node_fibrechannel_tx_frames_total{fc_host="host1"} 80 # HELP node_fibrechannel_tx_words_total Number of words transmitted by host port # TYPE node_fibrechannel_tx_words_total counter node_fibrechannel_tx_words_total{fc_host="host0"} 6 +node_fibrechannel_tx_words_total{fc_host="host1"} 96 # HELP node_filefd_allocated File descriptor statistics: allocated. # TYPE node_filefd_allocated gauge node_filefd_allocated 1024 @@ -2306,6 +2342,9 @@ node_netstat_TcpExt_SyncookiesSent 0 # HELP node_netstat_TcpExt_TCPOFOQueue Statistic TcpExtTCPOFOQueue. # TYPE node_netstat_TcpExt_TCPOFOQueue untyped node_netstat_TcpExt_TCPOFOQueue 42 +# HELP node_netstat_TcpExt_TCPRcvQDrop Statistic TcpExtTCPRcvQDrop. +# TYPE node_netstat_TcpExt_TCPRcvQDrop untyped +node_netstat_TcpExt_TCPRcvQDrop 131 # HELP node_netstat_TcpExt_TCPTimeouts Statistic TcpExtTCPTimeouts. # TYPE node_netstat_TcpExt_TCPTimeouts untyped node_netstat_TcpExt_TCPTimeouts 115 @@ -2814,6 +2853,9 @@ node_pressure_io_stalled_seconds_total 159.229614 # HELP node_pressure_io_waiting_seconds_total Total time in seconds that processes have waited due to IO congestion # TYPE node_pressure_io_waiting_seconds_total counter node_pressure_io_waiting_seconds_total 159.886802 +# HELP node_pressure_irq_stalled_seconds_total Total time in seconds no process could make progress due to IRQ congestion +# TYPE node_pressure_irq_stalled_seconds_total counter +node_pressure_irq_stalled_seconds_total 0.008494 # HELP node_pressure_memory_stalled_seconds_total Total time in seconds no process could make progress due to memory congestion # TYPE node_pressure_memory_stalled_seconds_total counter node_pressure_memory_stalled_seconds_total 0 @@ -3741,6 +3783,9 @@ node_zfs_arc_l2_writes_lock_retry 0 # HELP node_zfs_arc_l2_writes_sent kstat.zfs.misc.arcstats.l2_writes_sent # TYPE node_zfs_arc_l2_writes_sent untyped node_zfs_arc_l2_writes_sent 0 +# HELP node_zfs_arc_memory_available_bytes kstat.zfs.misc.arcstats.memory_available_bytes +# TYPE node_zfs_arc_memory_available_bytes untyped +node_zfs_arc_memory_available_bytes -9.223372036854776e+17 # HELP node_zfs_arc_memory_direct_count kstat.zfs.misc.arcstats.memory_direct_count # TYPE node_zfs_arc_memory_direct_count untyped node_zfs_arc_memory_direct_count 542 @@ -4548,6 +4593,10 @@ node_zoneinfo_spanned_pages{node="0",zone="Normal"} 7.806976e+06 # TYPE process_cpu_seconds_total counter # HELP process_max_fds Maximum number of open file descriptors. # TYPE process_max_fds gauge +# HELP process_network_receive_bytes_total Number of bytes received by the process over the network. +# TYPE process_network_receive_bytes_total counter +# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network. +# TYPE process_network_transmit_bytes_total counter # HELP process_open_fds Number of open file descriptors. # TYPE process_open_fds gauge # HELP process_resident_memory_bytes Resident memory size in bytes. diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 5b6cfbe1..a80f6b63 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -1,55 +1,59 @@ -# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles. # TYPE go_gc_duration_seconds summary +# HELP go_gc_gogc_percent Heap size target percentage configured by the user, otherwise 100. This value is set by the GOGC environment variable, and the runtime/debug.SetGCPercent function. Sourced from /gc/gogc:percent +# TYPE go_gc_gogc_percent gauge +# HELP go_gc_gomemlimit_bytes Go runtime memory limit configured by the user, otherwise math.MaxInt64. This value is set by the GOMEMLIMIT environment variable, and the runtime/debug.SetMemoryLimit function. Sourced from /gc/gomemlimit:bytes +# TYPE go_gc_gomemlimit_bytes gauge # HELP go_goroutines Number of goroutines that currently exist. # TYPE go_goroutines gauge # HELP go_info Information about the Go environment. # TYPE go_info gauge -# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# HELP go_memstats_alloc_bytes Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes. # TYPE go_memstats_alloc_bytes gauge -# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes. # TYPE go_memstats_alloc_bytes_total counter -# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes. # TYPE go_memstats_buck_hash_sys_bytes gauge -# HELP go_memstats_frees_total Total number of frees. +# HELP go_memstats_frees_total Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects. # TYPE go_memstats_frees_total counter -# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes. # TYPE go_memstats_gc_sys_bytes gauge -# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. +# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes. # TYPE go_memstats_heap_alloc_bytes gauge -# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. +# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes. # TYPE go_memstats_heap_idle_bytes gauge -# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. +# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes # TYPE go_memstats_heap_inuse_bytes gauge -# HELP go_memstats_heap_objects Number of allocated objects. +# HELP go_memstats_heap_objects Number of currently allocated objects. Equals to /gc/heap/objects:objects. # TYPE go_memstats_heap_objects gauge -# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. +# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes. # TYPE go_memstats_heap_released_bytes gauge -# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. +# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes. # TYPE go_memstats_heap_sys_bytes gauge # HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. # TYPE go_memstats_last_gc_time_seconds gauge -# HELP go_memstats_lookups_total Total number of pointer lookups. -# TYPE go_memstats_lookups_total counter -# HELP go_memstats_mallocs_total Total number of mallocs. +# HELP go_memstats_mallocs_total Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects. # TYPE go_memstats_mallocs_total counter -# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. +# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes. # TYPE go_memstats_mcache_inuse_bytes gauge -# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. +# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes. # TYPE go_memstats_mcache_sys_bytes gauge -# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. +# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes. # TYPE go_memstats_mspan_inuse_bytes gauge -# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. +# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes. # TYPE go_memstats_mspan_sys_bytes gauge -# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. +# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes. # TYPE go_memstats_next_gc_bytes gauge -# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. +# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes. # TYPE go_memstats_other_sys_bytes gauge -# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. +# HELP go_memstats_stack_inuse_bytes Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes. # TYPE go_memstats_stack_inuse_bytes gauge -# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. +# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes. # TYPE go_memstats_stack_sys_bytes gauge -# HELP go_memstats_sys_bytes Number of bytes obtained from system. +# HELP go_memstats_sys_bytes Number of bytes obtained from system. Equals to /memory/classes/total:byte. # TYPE go_memstats_sys_bytes gauge +# HELP go_sched_gomaxprocs_threads The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously. Sourced from /sched/gomaxprocs:threads +# TYPE go_sched_gomaxprocs_threads gauge # HELP go_threads Number of OS threads created. # TYPE go_threads gauge # HELP node_arp_entries ARP entries by device @@ -161,6 +165,14 @@ node_btrfs_allocation_ratio{block_group_type="metadata",mode="raid1",uuid="0abb2 node_btrfs_allocation_ratio{block_group_type="metadata",mode="raid6",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 2 node_btrfs_allocation_ratio{block_group_type="system",mode="raid1",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 2 node_btrfs_allocation_ratio{block_group_type="system",mode="raid6",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 2 +# HELP node_btrfs_commit_seconds_total Sum of the duration of all commits, in seconds. +# TYPE node_btrfs_commit_seconds_total counter +node_btrfs_commit_seconds_total{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 47836.09 +node_btrfs_commit_seconds_total{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 +# HELP node_btrfs_commits_total The total number of commits that have occurred. +# TYPE node_btrfs_commits_total counter +node_btrfs_commits_total{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 258051 +node_btrfs_commits_total{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 # HELP node_btrfs_device_size_bytes Size of a device that is part of the filesystem. # TYPE node_btrfs_device_size_bytes gauge node_btrfs_device_size_bytes{device="loop22",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1.073741824e+10 @@ -177,6 +189,14 @@ node_btrfs_global_rsv_size_bytes{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1. # TYPE node_btrfs_info gauge node_btrfs_info{label="",uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 1 node_btrfs_info{label="fixture",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 1 +# HELP node_btrfs_last_commit_seconds Duration of the most recent commit, in seconds. +# TYPE node_btrfs_last_commit_seconds gauge +node_btrfs_last_commit_seconds{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 1 +node_btrfs_last_commit_seconds{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 +# HELP node_btrfs_max_commit_seconds Duration of the slowest commit, in seconds. +# TYPE node_btrfs_max_commit_seconds gauge +node_btrfs_max_commit_seconds{uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 51.462 +node_btrfs_max_commit_seconds{uuid="7f07c59f-6136-449c-ab87-e1cf2328731b"} 0 # HELP node_btrfs_reserved_bytes Amount of space reserved for a data type # TYPE node_btrfs_reserved_bytes gauge node_btrfs_reserved_bytes{block_group_type="data",uuid="0abb23a9-579b-43e6-ad30-227ef47fcb9d"} 0 @@ -764,48 +784,64 @@ node_entropy_available_bits 1337 node_entropy_pool_size_bits 4096 # HELP node_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build. # TYPE node_exporter_build_info gauge +# HELP node_fibrechannel_dumped_frames_total Number of dumped frames +# TYPE node_fibrechannel_dumped_frames_total counter +node_fibrechannel_dumped_frames_total{fc_host="host1"} 0 # HELP node_fibrechannel_error_frames_total Number of errors in frames # TYPE node_fibrechannel_error_frames_total counter node_fibrechannel_error_frames_total{fc_host="host0"} 0 +node_fibrechannel_error_frames_total{fc_host="host1"} 19 # HELP node_fibrechannel_fcp_packet_aborts_total Number of aborted packets # TYPE node_fibrechannel_fcp_packet_aborts_total counter node_fibrechannel_fcp_packet_aborts_total{fc_host="host0"} 19 # HELP node_fibrechannel_info Non-numeric data from /sys/class/fc_host/, value is always 1. # TYPE node_fibrechannel_info gauge +node_fibrechannel_info{dev_loss_tmo="",fabric_name="",fc_host="host1",port_id="",port_name="",port_state="",port_type="",speed="8 Gbit",supported_classes="",supported_speeds="",symbolic_name=""} 1 node_fibrechannel_info{dev_loss_tmo="30",fabric_name="0",fc_host="host0",port_id="000002",port_name="1000e0071bce95f2",port_state="Online",port_type="Point-To-Point (direct nport connection)",speed="16 Gbit",supported_classes="Class 3",supported_speeds="4 Gbit, 8 Gbit, 16 Gbit",symbolic_name="Emulex SN1100E2P FV12.4.270.3 DV12.4.0.0. HN:gotest. OS:Linux"} 1 # HELP node_fibrechannel_invalid_crc_total Invalid Cyclic Redundancy Check count # TYPE node_fibrechannel_invalid_crc_total counter node_fibrechannel_invalid_crc_total{fc_host="host0"} 2 +node_fibrechannel_invalid_crc_total{fc_host="host1"} 32 # HELP node_fibrechannel_invalid_tx_words_total Number of invalid words transmitted by host port # TYPE node_fibrechannel_invalid_tx_words_total counter node_fibrechannel_invalid_tx_words_total{fc_host="host0"} 8 +node_fibrechannel_invalid_tx_words_total{fc_host="host1"} 128 # HELP node_fibrechannel_link_failure_total Number of times the host port link has failed # TYPE node_fibrechannel_link_failure_total counter node_fibrechannel_link_failure_total{fc_host="host0"} 9 +node_fibrechannel_link_failure_total{fc_host="host1"} 144 # HELP node_fibrechannel_loss_of_signal_total Number of times signal has been lost # TYPE node_fibrechannel_loss_of_signal_total counter node_fibrechannel_loss_of_signal_total{fc_host="host0"} 17 +node_fibrechannel_loss_of_signal_total{fc_host="host1"} 272 # HELP node_fibrechannel_loss_of_sync_total Number of failures on either bit or transmission word boundaries # TYPE node_fibrechannel_loss_of_sync_total counter node_fibrechannel_loss_of_sync_total{fc_host="host0"} 16 +node_fibrechannel_loss_of_sync_total{fc_host="host1"} 256 # HELP node_fibrechannel_nos_total Number Not_Operational Primitive Sequence received by host port # TYPE node_fibrechannel_nos_total counter node_fibrechannel_nos_total{fc_host="host0"} 18 +node_fibrechannel_nos_total{fc_host="host1"} 288 # HELP node_fibrechannel_rx_frames_total Number of frames received # TYPE node_fibrechannel_rx_frames_total counter node_fibrechannel_rx_frames_total{fc_host="host0"} 3 +node_fibrechannel_rx_frames_total{fc_host="host1"} 48 # HELP node_fibrechannel_rx_words_total Number of words received by host port # TYPE node_fibrechannel_rx_words_total counter node_fibrechannel_rx_words_total{fc_host="host0"} 4 +node_fibrechannel_rx_words_total{fc_host="host1"} 64 # HELP node_fibrechannel_seconds_since_last_reset_total Number of seconds since last host port reset # TYPE node_fibrechannel_seconds_since_last_reset_total counter node_fibrechannel_seconds_since_last_reset_total{fc_host="host0"} 7 +node_fibrechannel_seconds_since_last_reset_total{fc_host="host1"} 112 # HELP node_fibrechannel_tx_frames_total Number of frames transmitted by host port # TYPE node_fibrechannel_tx_frames_total counter node_fibrechannel_tx_frames_total{fc_host="host0"} 5 +node_fibrechannel_tx_frames_total{fc_host="host1"} 80 # HELP node_fibrechannel_tx_words_total Number of words transmitted by host port # TYPE node_fibrechannel_tx_words_total counter node_fibrechannel_tx_words_total{fc_host="host0"} 6 +node_fibrechannel_tx_words_total{fc_host="host1"} 96 # HELP node_filefd_allocated File descriptor statistics: allocated. # TYPE node_filefd_allocated gauge node_filefd_allocated 1024 @@ -2328,6 +2364,9 @@ node_netstat_TcpExt_SyncookiesSent 0 # HELP node_netstat_TcpExt_TCPOFOQueue Statistic TcpExtTCPOFOQueue. # TYPE node_netstat_TcpExt_TCPOFOQueue untyped node_netstat_TcpExt_TCPOFOQueue 42 +# HELP node_netstat_TcpExt_TCPRcvQDrop Statistic TcpExtTCPRcvQDrop. +# TYPE node_netstat_TcpExt_TCPRcvQDrop untyped +node_netstat_TcpExt_TCPRcvQDrop 131 # HELP node_netstat_TcpExt_TCPTimeouts Statistic TcpExtTCPTimeouts. # TYPE node_netstat_TcpExt_TCPTimeouts untyped node_netstat_TcpExt_TCPTimeouts 115 @@ -2836,6 +2875,9 @@ node_pressure_io_stalled_seconds_total 159.229614 # HELP node_pressure_io_waiting_seconds_total Total time in seconds that processes have waited due to IO congestion # TYPE node_pressure_io_waiting_seconds_total counter node_pressure_io_waiting_seconds_total 159.886802 +# HELP node_pressure_irq_stalled_seconds_total Total time in seconds no process could make progress due to IRQ congestion +# TYPE node_pressure_irq_stalled_seconds_total counter +node_pressure_irq_stalled_seconds_total 0.008494 # HELP node_pressure_memory_stalled_seconds_total Total time in seconds no process could make progress due to memory congestion # TYPE node_pressure_memory_stalled_seconds_total counter node_pressure_memory_stalled_seconds_total 0 @@ -3763,6 +3805,9 @@ node_zfs_arc_l2_writes_lock_retry 0 # HELP node_zfs_arc_l2_writes_sent kstat.zfs.misc.arcstats.l2_writes_sent # TYPE node_zfs_arc_l2_writes_sent untyped node_zfs_arc_l2_writes_sent 0 +# HELP node_zfs_arc_memory_available_bytes kstat.zfs.misc.arcstats.memory_available_bytes +# TYPE node_zfs_arc_memory_available_bytes untyped +node_zfs_arc_memory_available_bytes -9.223372036854776e+17 # HELP node_zfs_arc_memory_direct_count kstat.zfs.misc.arcstats.memory_direct_count # TYPE node_zfs_arc_memory_direct_count untyped node_zfs_arc_memory_direct_count 542 @@ -4570,6 +4615,10 @@ node_zoneinfo_spanned_pages{node="0",zone="Normal"} 7.806976e+06 # TYPE process_cpu_seconds_total counter # HELP process_max_fds Maximum number of open file descriptors. # TYPE process_max_fds gauge +# HELP process_network_receive_bytes_total Number of bytes received by the process over the network. +# TYPE process_network_receive_bytes_total counter +# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network. +# TYPE process_network_transmit_bytes_total counter # HELP process_open_fds Number of open file descriptors. # TYPE process_open_fds gauge # HELP process_resident_memory_bytes Resident memory size in bytes. diff --git a/collector/fixtures/proc/1/mountinfo b/collector/fixtures/proc/1/mountinfo new file mode 100644 index 00000000..11c50a6f --- /dev/null +++ b/collector/fixtures/proc/1/mountinfo @@ -0,0 +1,31 @@ +24 29 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw +25 29 0:23 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw +26 29 0:5 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=7978892k,nr_inodes=1994723,mode=755 +27 26 0:24 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000 +28 29 0:25 / /run rw,nosuid,relatime shared:5 - tmpfs tmpfs rw,size=1617716k,mode=755 +29 1 259:2 / / rw,relatime shared:1 - ext4 /dev/dm-2 errors=remount-ro,data=ordered +30 24 0:6 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw +31 26 0:26 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw,inode64 +32 28 0:27 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k +33 24 0:28 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755 +34 31 0:24 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd +35 32 0:25 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:11 - pstore pstore rw +36 33 0:26 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuset +37 34 0:27 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,cpu,cpuacct +38 35 0:28 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,devices +39 36 0:29 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,freezer +40 37 0:30 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,net_cls,net_prio +41 38 0:31 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,blkio +42 39 0:32 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,perf_event +43 40 0:33 / /proc/sys/fs/binfmt_misc rw,relatime shared:21 - systemd-1 autofs rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +44 41 0:34 / /dev/mqueue rw,relatime shared:22 - mqueue mqueue rw +45 42 0:35 / /sys/kernel/debug rw,relatime shared:23 - debugfs debugfs rw +46 43 0:36 / /dev/hugepages rw,relatime shared:24 - hugetlbfs hugetlbfs rw +47 44 0:37 / /sys/fs/fuse/connections rw,relatime shared:25 - fusectl fusectl rw +48 45 260:3 / /boot rw,relatime shared:92 - ext2 /dev/sda3 rw +49 46 0:39 / /run/rpc_pipefs rw,relatime shared:27 - rpc_pipefs rpc_pipefs rw +265 37 0:41 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime shared:94 - binfmt_misc binfmt_misc rw +3002 28 0:79 / /run/user/1000 rw,nosuid,nodev,relatime shared:1225 - tmpfs tmpfs rw,size=1603436k,nr_inodes=400859,mode=700,uid=1000,gid=1000 +3147 3002 0:81 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:1290 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000 +3148 3003 260:0 / /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\040bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk rw,relatime shared:31 - ext4 /dev/sda rw,data=ordered +3149 3004 260:0 / /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\011bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk rw,relatime shared:32 - ext4 /dev/sda rw,data=ordered diff --git a/collector/fixtures/proc/1/mounts b/collector/fixtures/proc/1/mounts deleted file mode 100644 index 7452d495..00000000 --- a/collector/fixtures/proc/1/mounts +++ /dev/null @@ -1,32 +0,0 @@ -rootfs / rootfs rw 0 0 -sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 -proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 -udev /dev devtmpfs rw,relatime,size=10240k,nr_inodes=1008585,mode=755 0 0 -devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0 -tmpfs /run tmpfs rw,nosuid,relatime,size=1617716k,mode=755 0 0 -/dev/dm-2 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0 -securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0 -tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0 -tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0 -tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0 -cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd 0 0 -pstore /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0 -cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0 -cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0 -cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0 -cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0 -cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0 -cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0 -cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0 -systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct 0 0 -mqueue /dev/mqueue mqueue rw,relatime 0 0 -debugfs /sys/kernel/debug debugfs rw,relatime 0 0 -hugetlbfs /dev/hugepages hugetlbfs rw,relatime 0 0 -fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0 -/dev/sda3 /boot ext2 rw,relatime 0 0 -rpc_pipefs /run/rpc_pipefs rpc_pipefs rw,relatime 0 0 -binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0 -tmpfs /run/user/1000 tmpfs rw,nosuid,nodev,relatime,size=808860k,mode=700,uid=1000,gid=1000 0 0 -gvfsd-fuse /run/user/1000/gvfs fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0 -/dev/sda /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\040bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk ext4 rw,relatime,data=ordered 0 0 -/dev/sda /var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore]\011bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk ext4 rw,relatime,data=ordered 0 0 diff --git a/collector/fixtures/proc/net/netstat b/collector/fixtures/proc/net/netstat index de88470b..a504f800 100644 --- a/collector/fixtures/proc/net/netstat +++ b/collector/fixtures/proc/net/netstat @@ -1,4 +1,4 @@ -TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLoss TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPRenoRecoveryFail TCPSackRecoveryFail TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPChallengeACK TCPSYNChallenge TCPOFOQueue -TcpExt: 0 0 2 0 0 0 0 0 0 0 388812 0 0 0 0 6 102471 17 9 0 0 80568 0 168808 0 4471289 26 1433940 3744565 0 1 0 0 0 0 0 0 0 0 48 0 0 0 1 0 1 0 1 115 0 0 0 0 9 0 5 0 41 4 0 0 0 0 0 0 0 1 0 0 0 0 2 5 0 0 0 0 0 0 0 2 2 42 +TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLoss TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPRenoRecoveryFail TCPSackRecoveryFail TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPChallengeACK TCPSYNChallenge TCPOFOQueue TCPRcvQDrop +TcpExt: 0 0 2 0 0 0 0 0 0 0 388812 0 0 0 0 6 102471 17 9 0 0 80568 0 168808 0 4471289 26 1433940 3744565 0 1 0 0 0 0 0 0 0 0 48 0 0 0 1 0 1 0 1 115 0 0 0 0 9 0 5 0 41 4 0 0 0 0 0 0 0 1 0 0 0 0 2 5 0 0 0 0 0 0 0 2 2 42 131 IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets IpExt: 0 0 0 0 0 0 6286396970 2786264347 0 0 0 0 diff --git a/collector/fixtures/proc/pressure/cpu b/collector/fixtures/proc/pressure/cpu index 14acc3a3..0aaced93 100644 --- a/collector/fixtures/proc/pressure/cpu +++ b/collector/fixtures/proc/pressure/cpu @@ -1 +1,2 @@ some avg10=0.00 avg60=0.00 avg300=0.00 total=14036781 +full avg10=0.00 avg60=0.00 avg300=0.00 total=0 diff --git a/collector/fixtures/proc/pressure/irq b/collector/fixtures/proc/pressure/irq new file mode 100644 index 00000000..76059c75 --- /dev/null +++ b/collector/fixtures/proc/pressure/irq @@ -0,0 +1 @@ +full avg10=0.00 avg60=0.00 avg300=0.00 total=8494 \ No newline at end of file diff --git a/collector/fixtures/proc/spl/kstat/zfs/arcstats b/collector/fixtures/proc/spl/kstat/zfs/arcstats index 48a73a2c..1dbeed6f 100644 --- a/collector/fixtures/proc/spl/kstat/zfs/arcstats +++ b/collector/fixtures/proc/spl/kstat/zfs/arcstats @@ -1,93 +1,94 @@ 6 1 0x01 91 4368 5266997922 97951858082072 name type data -hits 4 8772612 -misses 4 604635 +anon_evictable_data 4 0 +anon_evictable_metadata 4 0 +anon_size 4 1917440 +arc_loaned_bytes 4 0 +arc_meta_limit 4 6275982336 +arc_meta_max 4 449286096 +arc_meta_min 4 16777216 +arc_meta_used 4 308103632 +arc_need_free 4 0 +arc_no_grow 4 0 +arc_prune 4 0 +arc_sys_free 4 261496832 +arc_tempreserve 4 0 +c 4 1643208777 +c_max 4 8367976448 +c_min 4 33554432 +data_size 4 1295836160 +deleted 4 60403 demand_data_hits 4 7221032 demand_data_misses 4 73300 demand_metadata_hits 4 1464353 demand_metadata_misses 4 498170 -prefetch_data_hits 4 3615 -prefetch_data_misses 4 17094 -prefetch_metadata_hits 4 83612 -prefetch_metadata_misses 4 16071 -mru_hits 4 855535 -mru_ghost_hits 4 21100 -mfu_hits 4 7829854 -mfu_ghost_hits 4 821 -deleted 4 60403 -mutex_miss 4 2 -evict_skip 4 2265729 -evict_not_enough 4 680 +duplicate_buffers 4 0 +duplicate_buffers_size 4 0 +duplicate_reads 4 0 evict_l2_cached 4 0 evict_l2_eligible 4 8992514560 evict_l2_ineligible 4 992552448 evict_l2_skip 4 0 +evict_not_enough 4 680 +evict_skip 4 2265729 +hash_chain_max 4 3 +hash_chains 4 412 +hash_collisions 4 50564 hash_elements 4 42359 hash_elements_max 4 88245 -hash_collisions 4 50564 -hash_chains 4 412 -hash_chain_max 4 3 -p 4 516395305 -c 4 1643208777 -c_min 4 33554432 -c_max 4 8367976448 -size 4 1603939792 hdr_size 4 16361080 -data_size 4 1295836160 -metadata_size 4 175298560 -other_size 4 116443992 -anon_size 4 1917440 -anon_evictable_data 4 0 -anon_evictable_metadata 4 0 -mru_size 4 402593792 -mru_evictable_data 4 278091264 -mru_evictable_metadata 4 18606592 -mru_ghost_size 4 999728128 -mru_ghost_evictable_data 4 883765248 -mru_ghost_evictable_metadata 4 115962880 -mfu_size 4 1066623488 -mfu_evictable_data 4 1017613824 -mfu_evictable_metadata 4 9163776 -mfu_ghost_size 4 104936448 -mfu_ghost_evictable_data 4 96731136 -mfu_ghost_evictable_metadata 4 8205312 -l2_hits 4 0 -l2_misses 4 0 +hits 4 8772612 +l2_abort_lowmem 4 0 +l2_asize 4 0 +l2_cdata_free_on_write 4 0 +l2_cksum_bad 4 0 +l2_compress_failures 4 0 +l2_compress_successes 4 0 +l2_compress_zeros 4 0 +l2_evict_l1cached 4 0 +l2_evict_lock_retry 4 0 +l2_evict_reading 4 0 l2_feeds 4 0 -l2_rw_clash 4 0 +l2_free_on_write 4 0 +l2_hdr_size 4 0 +l2_hits 4 0 +l2_io_error 4 0 +l2_misses 4 0 l2_read_bytes 4 0 +l2_rw_clash 4 0 +l2_size 4 0 l2_write_bytes 4 0 -l2_writes_sent 4 0 l2_writes_done 4 0 l2_writes_error 4 0 l2_writes_lock_retry 4 0 -l2_evict_lock_retry 4 0 -l2_evict_reading 4 0 -l2_evict_l1cached 4 0 -l2_free_on_write 4 0 -l2_cdata_free_on_write 4 0 -l2_abort_lowmem 4 0 -l2_cksum_bad 4 0 -l2_io_error 4 0 -l2_size 4 0 -l2_asize 4 0 -l2_hdr_size 4 0 -l2_compress_successes 4 0 -l2_compress_zeros 4 0 -l2_compress_failures 4 0 -memory_throttle_count 4 0 -duplicate_buffers 4 0 -duplicate_buffers_size 4 0 -duplicate_reads 4 0 +l2_writes_sent 4 0 +memory_available_bytes 3 -922337203685477580 memory_direct_count 4 542 memory_indirect_count 4 3006 -arc_no_grow 4 0 -arc_tempreserve 4 0 -arc_loaned_bytes 4 0 -arc_prune 4 0 -arc_meta_used 4 308103632 -arc_meta_limit 4 6275982336 -arc_meta_max 4 449286096 -arc_meta_min 4 16777216 -arc_need_free 4 0 -arc_sys_free 4 261496832 +memory_throttle_count 4 0 +metadata_size 4 175298560 +mfu_evictable_data 4 1017613824 +mfu_evictable_metadata 4 9163776 +mfu_ghost_evictable_data 4 96731136 +mfu_ghost_evictable_metadata 4 8205312 +mfu_ghost_hits 4 821 +mfu_ghost_size 4 104936448 +mfu_hits 4 7829854 +mfu_size 4 1066623488 +misses 4 604635 +mru_evictable_data 4 278091264 +mru_evictable_metadata 4 18606592 +mru_ghost_evictable_data 4 883765248 +mru_ghost_evictable_metadata 4 115962880 +mru_ghost_hits 4 21100 +mru_ghost_size 4 999728128 +mru_hits 4 855535 +mru_size 4 402593792 +mutex_miss 4 2 +other_size 4 116443992 +p 4 516395305 +prefetch_data_hits 4 3615 +prefetch_data_misses 4 17094 +prefetch_metadata_hits 4 83612 +prefetch_metadata_misses 4 16071 +size 4 1603939792 diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 0573cfd3..6fcf094d 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -288,6 +288,87 @@ Lines: 1 Emulex SN1100E2P FV12.4.270.3 DV12.4.0.0. HN:gotest. OS:Linux Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/fc_host/host1 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/speed +Lines: 1 +8 Gbit +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/fc_host/host1/statistics +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/dumped_frames +Lines: 1 +0x0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/error_frames +Lines: 1 +0x13 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/fcp_packet_aborts +Lines: 1 +0xffffffffffffffff +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/invalid_crc_count +Lines: 1 +0x20 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/invalid_tx_word_count +Lines: 1 +0x80 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/link_failure_count +Lines: 1 +0x90 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/loss_of_signal_count +Lines: 1 +0x110 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/loss_of_sync_count +Lines: 1 +0x100 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/nos_count +Lines: 1 +0x120 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/rx_frames +Lines: 1 +0x30 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/rx_words +Lines: 1 +0x40 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/seconds_since_last_reset +Lines: 1 +0x70 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/tx_frames +Lines: 1 +0x50 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/fc_host/host1/statistics/tx_words +Lines: 1 +0x60 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/class/hwmon Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4408,6 +4489,14 @@ Lines: 1 4096 Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/commit_stats +Lines: 4 +commits 258051 +last_commit_ms 1000 +max_commit_ms 51462 +total_commit_ms 47836090EOF +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/collector/fixtures/textfile/metrics_merge_different_help.out b/collector/fixtures/textfile/metrics_merge_different_help.out index ba161f07..b9385a75 100644 --- a/collector/fixtures/textfile/metrics_merge_different_help.out +++ b/collector/fixtures/textfile/metrics_merge_different_help.out @@ -8,4 +8,4 @@ node_textfile_mtime_seconds{file="fixtures/textfile/metrics_merge_different_help node_textfile_mtime_seconds{file="fixtures/textfile/metrics_merge_different_help/b.prom"} 1 # HELP node_textfile_scrape_error 1 if there was an error opening or reading a file, 0 otherwise # TYPE node_textfile_scrape_error gauge -node_textfile_scrape_error 0 +node_textfile_scrape_error 1 diff --git a/collector/fixtures_bindmount/proc/1/mountinfo b/collector/fixtures_bindmount/proc/1/mountinfo new file mode 100644 index 00000000..e4f1bcc5 --- /dev/null +++ b/collector/fixtures_bindmount/proc/1/mountinfo @@ -0,0 +1,6 @@ +29 1 259:0 / /host rw,seclabel,relatime,data=ordered shared:1 - ext4 /dev/nvme1n0 rw +30 1 260:0 / /host/media/volume1 rw,seclabel,relatime,data=ordered shared:1 - ext4 /dev/nvme1n1 rw +31 1 261:0 / /host/media/volume2 rw,seclabel,relatime,data=ordered shared:1 - ext4 /dev/nvme1n2 rw +31 26 0:26 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw,inode64 +32 28 0:27 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k,inode64 +33 24 0:28 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw diff --git a/collector/fixtures_bindmount/proc/mounts b/collector/fixtures_bindmount/proc/mounts deleted file mode 100644 index 32f9567e..00000000 --- a/collector/fixtures_bindmount/proc/mounts +++ /dev/null @@ -1,6 +0,0 @@ -/dev/nvme1n0 /host ext4 rw,seclabel,relatime,data=ordered 0 0 -/dev/nvme1n1 /host/media/volume1 ext4 rw,seclabel,relatime,data=ordered 0 0 -/dev/nvme1n2 /host/media/volume2 ext4 rw,seclabel,relatime,data=ordered 0 0 -tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0 -tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0 -tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0 diff --git a/collector/fixtures_hidepid/proc/mounts b/collector/fixtures_hidepid/proc/mounts deleted file mode 100644 index fb6a9635..00000000 --- a/collector/fixtures_hidepid/proc/mounts +++ /dev/null @@ -1 +0,0 @@ -rootfs / rootfs rw 0 0 diff --git a/collector/fixtures_hidepid/proc/self/mountinfo b/collector/fixtures_hidepid/proc/self/mountinfo new file mode 100644 index 00000000..dde22f67 --- /dev/null +++ b/collector/fixtures_hidepid/proc/self/mountinfo @@ -0,0 +1 @@ +29 1 259:2 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p2 rw,errors=remount-ro diff --git a/collector/hwmon_linux.go b/collector/hwmon_linux.go index a8086d30..9c0e065c 100644 --- a/collector/hwmon_linux.go +++ b/collector/hwmon_linux.go @@ -18,6 +18,8 @@ package collector import ( "errors" + "fmt" + "log/slog" "os" "path/filepath" "regexp" @@ -25,15 +27,15 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) var ( - collectorHWmonChipInclude = kingpin.Flag("collector.hwmon.chip-include", "Regexp of hwmon chip to include (mutually exclusive to device-exclude).").String() - collectorHWmonChipExclude = kingpin.Flag("collector.hwmon.chip-exclude", "Regexp of hwmon chip to exclude (mutually exclusive to device-include).").String() + collectorHWmonChipInclude = kingpin.Flag("collector.hwmon.chip-include", "Regexp of hwmon chip to include (mutually exclusive to device-exclude).").String() + collectorHWmonChipExclude = kingpin.Flag("collector.hwmon.chip-exclude", "Regexp of hwmon chip to exclude (mutually exclusive to device-include).").String() + collectorHWmonSensorInclude = kingpin.Flag("collector.hwmon.sensor-include", "Regexp of hwmon sensor to include (mutually exclusive to sensor-exclude).").String() + collectorHWmonSensorExclude = kingpin.Flag("collector.hwmon.sensor-exclude", "Regexp of hwmon sensor to exclude (mutually exclusive to sensor-include).").String() hwmonInvalidMetricChars = regexp.MustCompile("[^a-z0-9:_]") hwmonFilenameFormat = regexp.MustCompile(`^(?P[^0-9]+)(?P[0-9]*)?(_(?P.+))?$`) @@ -52,16 +54,18 @@ func init() { type hwMonCollector struct { deviceFilter deviceFilter - logger log.Logger + sensorFilter deviceFilter + logger *slog.Logger } // NewHwMonCollector returns a new Collector exposing /sys/class/hwmon stats // (similar to lm-sensors). -func NewHwMonCollector(logger log.Logger) (Collector, error) { +func NewHwMonCollector(logger *slog.Logger) (Collector, error) { return &hwMonCollector{ logger: logger, deviceFilter: newDeviceFilter(*collectorHWmonChipExclude, *collectorHWmonChipInclude), + sensorFilter: newDeviceFilter(*collectorHWmonSensorExclude, *collectorHWmonSensorInclude), }, nil } @@ -104,6 +108,9 @@ func sysReadFile(file string) ([]byte, error) { if err != nil { return nil, err } + if n < 0 { + return nil, fmt.Errorf("failed to read file: %q, read returned negative bytes value: %d", file, n) + } return b[:n], nil } @@ -164,7 +171,7 @@ func (c *hwMonCollector) updateHwmon(ch chan<- prometheus.Metric, dir string) er } if c.deviceFilter.ignored(hwmonName) { - level.Debug(c.logger).Log("msg", "ignoring hwmon chip", "chip", hwmonName) + c.logger.Debug("ignoring hwmon chip", "chip", hwmonName) return nil } @@ -202,6 +209,15 @@ func (c *hwMonCollector) updateHwmon(ch chan<- prometheus.Metric, dir string) er // Format all sensors. for sensor, sensorData := range data { + // Filtering for sensors is done on concatenated device name and sensor name + // separated by a semicolon. This allows for excluding or including of specific + // sensors on specific devices. For example, to exclude the sensor "temp3" on + // the device "platform_coretemp_0", use "platform_coretemp_0;temp3" + if c.sensorFilter.ignored(hwmonName + ";" + sensor) { + c.logger.Debug("ignoring sensor", "sensor", sensor) + continue + } + _, sensorType, _, _ := explodeSensorFilename(sensor) labels := []string{hwmonName, sensor} @@ -437,7 +453,7 @@ func (c *hwMonCollector) Update(ch chan<- prometheus.Metric) error { hwmonFiles, err := os.ReadDir(hwmonPathName) if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "hwmon collector metrics are not available for this system") + c.logger.Debug("hwmon collector metrics are not available for this system") return ErrNoData } diff --git a/collector/infiniband_linux.go b/collector/infiniband_linux.go index 21e2de46..2db0bae7 100644 --- a/collector/infiniband_linux.go +++ b/collector/infiniband_linux.go @@ -19,11 +19,10 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "strconv" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -31,7 +30,7 @@ import ( type infinibandCollector struct { fs sysfs.FS metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger subsystem string } @@ -40,7 +39,7 @@ func init() { } // NewInfiniBandCollector returns a new Collector exposing InfiniBand stats. -func NewInfiniBandCollector(logger log.Logger) (Collector, error) { +func NewInfiniBandCollector(logger *slog.Logger) (Collector, error) { var i infinibandCollector var err error @@ -148,7 +147,7 @@ func (c *infinibandCollector) Update(ch chan<- prometheus.Metric) error { devices, err := c.fs.InfiniBandClass() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "infiniband statistics not found, skipping") + c.logger.Debug("infiniband statistics not found, skipping") return ErrNoData } return fmt.Errorf("error obtaining InfiniBand class info: %w", err) diff --git a/collector/interrupts_common.go b/collector/interrupts_common.go index eea703f9..08baa106 100644 --- a/collector/interrupts_common.go +++ b/collector/interrupts_common.go @@ -18,27 +18,38 @@ package collector import ( - "github.com/go-kit/log" + "github.com/alecthomas/kingpin/v2" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) type interruptsCollector struct { - desc typedDesc - logger log.Logger + desc typedDesc + logger *slog.Logger + nameFilter deviceFilter + includeZeros bool } func init() { registerCollector("interrupts", defaultDisabled, NewInterruptsCollector) } +var ( + interruptsInclude = kingpin.Flag("collector.interrupts.name-include", "Regexp of interrupts name to include (mutually exclusive to --collector.interrupts.name-exclude).").String() + interruptsExclude = kingpin.Flag("collector.interrupts.name-exclude", "Regexp of interrupts name to exclude (mutually exclusive to --collector.interrupts.name-include).").String() + interruptsIncludeZeros = kingpin.Flag("collector.interrupts.include-zeros", "Include interrupts that have a zero value").Default("true").Bool() +) + // NewInterruptsCollector returns a new Collector exposing interrupts stats. -func NewInterruptsCollector(logger log.Logger) (Collector, error) { +func NewInterruptsCollector(logger *slog.Logger) (Collector, error) { return &interruptsCollector{ desc: typedDesc{prometheus.NewDesc( namespace+"_interrupts_total", "Interrupt details.", interruptLabelNames, nil, ), prometheus.CounterValue}, - logger: logger, + logger: logger, + nameFilter: newDeviceFilter(*interruptsExclude, *interruptsInclude), + includeZeros: *interruptsIncludeZeros, }, nil } diff --git a/collector/interrupts_linux.go b/collector/interrupts_linux.go index ede78191..77caf423 100644 --- a/collector/interrupts_linux.go +++ b/collector/interrupts_linux.go @@ -39,10 +39,19 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) (err error) { } for name, interrupt := range interrupts { for cpuNo, value := range interrupt.values { + filterName := name + ";" + interrupt.info + ";" + interrupt.devices + if c.nameFilter.ignored(filterName) { + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) + continue + } fv, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("invalid value %s in interrupts: %w", value, err) } + if !c.includeZeros && fv == 0.0 { + c.logger.Debug("ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + continue + } ch <- c.desc.mustNewConstMetric(fv, strconv.Itoa(cpuNo), name, interrupt.info, interrupt.devices) } } diff --git a/collector/interrupts_openbsd.go b/collector/interrupts_openbsd.go index 9fa5b68d..60fcbf1d 100644 --- a/collector/interrupts_openbsd.go +++ b/collector/interrupts_openbsd.go @@ -106,10 +106,20 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) error { } for dev, interrupt := range interrupts { for cpuNo, value := range interrupt.values { + interruptType := fmt.Sprintf("%d", interrupt.vector) + filterName := interruptType + ";" + dev + if c.nameFilter.ignored(filterName) { + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) + continue + } + if !c.includeZeros && value == 0.0 { + c.logger.Debug("ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + continue + } ch <- c.desc.mustNewConstMetric( value, strconv.Itoa(cpuNo), - strconv.Itoa(interrupt.vector), + interruptType, dev, ) } diff --git a/collector/interrupts_openbsd_amd64.go b/collector/interrupts_openbsd_amd64.go index ba1091c4..69851a22 100644 --- a/collector/interrupts_openbsd_amd64.go +++ b/collector/interrupts_openbsd_amd64.go @@ -77,10 +77,20 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) error { } for dev, interrupt := range interrupts { for cpuNo, value := range interrupt.values { + interruptType := fmt.Sprintf("%d", interrupt.vector) + filterName := interruptType + ";" + dev + if c.nameFilter.ignored(filterName) { + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) + continue + } + if !c.includeZeros && value == 0.0 { + c.logger.Debug("ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + continue + } ch <- c.desc.mustNewConstMetric( value, strconv.Itoa(cpuNo), - fmt.Sprintf("%d", interrupt.vector), + interruptType, dev, ) } diff --git a/collector/ipvs_linux.go b/collector/ipvs_linux.go index 63a3a1a8..7d7bae54 100644 --- a/collector/ipvs_linux.go +++ b/collector/ipvs_linux.go @@ -19,14 +19,13 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "sort" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -37,7 +36,7 @@ type ipvsCollector struct { backendLabels []string backendConnectionsActive, backendConnectionsInact, backendWeight typedDesc connections, incomingPackets, outgoingPackets, incomingBytes, outgoingBytes typedDesc - logger log.Logger + logger *slog.Logger } type ipvsBackendStatus struct { @@ -73,11 +72,11 @@ func init() { // NewIPVSCollector sets up a new collector for IPVS metrics. It accepts the // "procfs" config parameter to override the default proc location (/proc). -func NewIPVSCollector(logger log.Logger) (Collector, error) { +func NewIPVSCollector(logger *slog.Logger) (Collector, error) { return newIPVSCollector(logger) } -func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) { +func newIPVSCollector(logger *slog.Logger) (*ipvsCollector, error) { var ( c ipvsCollector err error @@ -143,7 +142,7 @@ func (c *ipvsCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { // Cannot access ipvs metrics, report no error. if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "ipvs collector metrics are not available for this system") + c.logger.Debug("ipvs collector metrics are not available for this system") return ErrNoData } return fmt.Errorf("could not get IPVS stats: %w", err) diff --git a/collector/ipvs_linux_test.go b/collector/ipvs_linux_test.go index 6ee41b29..bc3e40f9 100644 --- a/collector/ipvs_linux_test.go +++ b/collector/ipvs_linux_test.go @@ -19,14 +19,14 @@ package collector import ( "errors" "fmt" + "io" + "log/slog" "net/http" "net/http/httptest" "os" "strings" "testing" - "github.com/go-kit/log" - "github.com/alecthomas/kingpin/v2" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -114,7 +114,7 @@ func TestIPVSCollector(t *testing.T) { if _, err := kingpin.CommandLine.Parse(args); err != nil { t.Fatal(err) } - collector, err := newIPVSCollector(log.NewNopLogger()) + collector, err := newIPVSCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { if test.err == nil { t.Fatal(err) @@ -182,7 +182,7 @@ func TestIPVSCollectorResponse(t *testing.T) { if _, err := kingpin.CommandLine.Parse(args); err != nil { t.Fatal(err) } - collector, err := NewIPVSCollector(log.NewNopLogger()) + collector, err := NewIPVSCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Fatal(err) } diff --git a/collector/ksmd_linux.go b/collector/ksmd_linux.go index 6d4142ae..d504fe30 100644 --- a/collector/ksmd_linux.go +++ b/collector/ksmd_linux.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "path/filepath" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -31,7 +31,7 @@ var ( type ksmdCollector struct { metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -50,7 +50,7 @@ func getCanonicalMetricName(filename string) string { } // NewKsmdCollector returns a new Collector exposing kernel/system statistics. -func NewKsmdCollector(logger log.Logger) (Collector, error) { +func NewKsmdCollector(logger *slog.Logger) (Collector, error) { subsystem := "ksmd" descs := make(map[string]*prometheus.Desc) diff --git a/collector/lnstat_linux.go b/collector/lnstat_linux.go index b3c90dc0..43bd7e05 100644 --- a/collector/lnstat_linux.go +++ b/collector/lnstat_linux.go @@ -18,22 +18,22 @@ package collector import ( "fmt" + "log/slog" "strconv" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) type lnstatCollector struct { - logger log.Logger + logger *slog.Logger } func init() { registerCollector("lnstat", defaultDisabled, NewLnstatCollector) } -func NewLnstatCollector(logger log.Logger) (Collector, error) { +func NewLnstatCollector(logger *slog.Logger) (Collector, error) { return &lnstatCollector{logger}, nil } diff --git a/collector/loadavg.go b/collector/loadavg.go index cb7b2cb1..09b1df3a 100644 --- a/collector/loadavg.go +++ b/collector/loadavg.go @@ -11,23 +11,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !noloadavg -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix) && !noloadavg +// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix // +build !noloadavg package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) type loadavgCollector struct { metric []typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -35,7 +34,7 @@ func init() { } // NewLoadavgCollector returns a new Collector exposing load average stats. -func NewLoadavgCollector(logger log.Logger) (Collector, error) { +func NewLoadavgCollector(logger *slog.Logger) (Collector, error) { return &loadavgCollector{ metric: []typedDesc{ {prometheus.NewDesc(namespace+"_load1", "1m load average.", nil, nil), prometheus.GaugeValue}, @@ -52,7 +51,7 @@ func (c *loadavgCollector) Update(ch chan<- prometheus.Metric) error { return fmt.Errorf("couldn't get load: %w", err) } for i, load := range loads { - level.Debug(c.logger).Log("msg", "return load", "index", i, "load", load) + c.logger.Debug("return load", "index", i, "load", load) ch <- c.metric[i].mustNewConstMetric(load) } return err diff --git a/collector/loadavg_aix.go b/collector/loadavg_aix.go new file mode 100644 index 00000000..0f3db0d9 --- /dev/null +++ b/collector/loadavg_aix.go @@ -0,0 +1,30 @@ +// Copyright 2024 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 !noloadavg +// +build !noloadavg + +package collector + +import ( + "github.com/power-devops/perfstat" +) + +func getLoad() ([]float64, error) { + stat, err := perfstat.CpuTotalStat() + if err != nil { + return nil, err + } + + return []float64{float64(stat.LoadAvg1), float64(stat.LoadAvg5), float64(stat.LoadAvg15)}, nil +} diff --git a/collector/logind_linux.go b/collector/logind_linux.go index de5b0d18..f5606ee1 100644 --- a/collector/logind_linux.go +++ b/collector/logind_linux.go @@ -18,10 +18,10 @@ package collector import ( "fmt" + "log/slog" "os" "strconv" - "github.com/go-kit/log" "github.com/godbus/dbus/v5" "github.com/prometheus/client_golang/prometheus" ) @@ -46,7 +46,7 @@ var ( ) type logindCollector struct { - logger log.Logger + logger *slog.Logger } type logindDbus struct { @@ -86,7 +86,7 @@ func init() { } // NewLogindCollector returns a new Collector exposing logind statistics. -func NewLogindCollector(logger log.Logger) (Collector, error) { +func NewLogindCollector(logger *slog.Logger) (Collector, error) { return &logindCollector{logger}, nil } diff --git a/collector/mdadm_linux.go b/collector/mdadm_linux.go index 89c56b75..5f274ca2 100644 --- a/collector/mdadm_linux.go +++ b/collector/mdadm_linux.go @@ -19,16 +19,15 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) type mdadmCollector struct { - logger log.Logger + logger *slog.Logger } func init() { @@ -36,7 +35,7 @@ func init() { } // NewMdadmCollector returns a new Collector exposing raid statistics. -func NewMdadmCollector(logger log.Logger) (Collector, error) { +func NewMdadmCollector(logger *slog.Logger) (Collector, error) { return &mdadmCollector{logger}, nil } @@ -112,7 +111,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "Not collecting mdstat, file does not exist", "file", *procPath) + c.logger.Debug("Not collecting mdstat, file does not exist", "file", *procPath) return ErrNoData } @@ -120,7 +119,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error { } for _, mdStat := range mdStats { - level.Debug(c.logger).Log("msg", "collecting metrics for device", "device", mdStat.Name) + c.logger.Debug("collecting metrics for device", "device", mdStat.Name) stateVals := make(map[string]float64) stateVals[mdStat.ActivityState] = 1 diff --git a/collector/meminfo.go b/collector/meminfo.go index b59337dd..4eab27b1 100644 --- a/collector/meminfo.go +++ b/collector/meminfo.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || linux || openbsd || netbsd) && !nomeminfo -// +build darwin linux openbsd netbsd +//go:build (darwin || linux || openbsd || netbsd || aix) && !nomeminfo +// +build darwin linux openbsd netbsd aix // +build !nomeminfo package collector @@ -21,8 +21,6 @@ import ( "fmt" "strings" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -30,19 +28,10 @@ const ( memInfoSubsystem = "memory" ) -type meminfoCollector struct { - logger log.Logger -} - func init() { registerCollector("meminfo", defaultEnabled, NewMeminfoCollector) } -// NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { - return &meminfoCollector{logger}, nil -} - // Update calls (*meminfoCollector).getMemInfo to get the platform specific // memory metrics. func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) error { @@ -51,7 +40,7 @@ func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get meminfo: %w", err) } - level.Debug(c.logger).Log("msg", "Set node_mem", "memInfo", memInfo) + c.logger.Debug("Set node_mem", "memInfo", fmt.Sprintf("%v", memInfo)) for k, v := range memInfo { if strings.HasSuffix(k, "_total") { metricType = prometheus.CounterValue diff --git a/collector/meminfo_aix.go b/collector/meminfo_aix.go new file mode 100644 index 00000000..ff59105b --- /dev/null +++ b/collector/meminfo_aix.go @@ -0,0 +1,47 @@ +// Copyright 2024 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 !nomeminfo +// +build !nomeminfo + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" +) + +type meminfoCollector struct { + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + return &meminfoCollector{ + logger: logger, + }, nil +} + +func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { + stats, err := perfstat.MemoryTotalStat() + if err != nil { + return nil, err + } + + return map[string]float64{ + "total_bytes": float64(stats.RealTotal * 4096), + "free_bytes": float64(stats.RealFree * 4096), + "available_bytes": float64(stats.RealAvailable * 4096), + }, nil +} diff --git a/collector/meminfo_darwin.go b/collector/meminfo_darwin.go index bbc96937..b5b10b4b 100644 --- a/collector/meminfo_darwin.go +++ b/collector/meminfo_darwin.go @@ -24,11 +24,23 @@ import "C" import ( "encoding/binary" "fmt" + "log/slog" "unsafe" "golang.org/x/sys/unix" ) +type meminfoCollector struct { + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + return &meminfoCollector{ + logger: logger, + }, nil +} + func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { host := C.mach_host_self() infoCount := C.mach_msg_type_number_t(C.HOST_VM_INFO64_COUNT) diff --git a/collector/meminfo_linux.go b/collector/meminfo_linux.go index cee29502..98d5a5c0 100644 --- a/collector/meminfo_linux.go +++ b/collector/meminfo_linux.go @@ -17,59 +17,189 @@ package collector import ( - "bufio" "fmt" - "io" - "os" - "regexp" - "strconv" - "strings" + "log/slog" + + "github.com/prometheus/procfs" ) -var ( - reParens = regexp.MustCompile(`\((.*)\)`) -) +type meminfoCollector struct { + fs procfs.FS + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + fs, err := procfs.NewFS(*procPath) + if err != nil { + return nil, fmt.Errorf("failed to open procfs: %w", err) + } + + return &meminfoCollector{ + logger: logger, + fs: fs, + }, nil +} func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { - file, err := os.Open(procFilePath("meminfo")) + meminfo, err := c.fs.Meminfo() if err != nil { - return nil, err - } - defer file.Close() - - return parseMemInfo(file) -} - -func parseMemInfo(r io.Reader) (map[string]float64, error) { - var ( - memInfo = map[string]float64{} - scanner = bufio.NewScanner(r) - ) - - for scanner.Scan() { - line := scanner.Text() - parts := strings.Fields(line) - // Workaround for empty lines occasionally occur in CentOS 6.2 kernel 3.10.90. - if len(parts) == 0 { - continue - } - fv, err := strconv.ParseFloat(parts[1], 64) - if err != nil { - return nil, fmt.Errorf("invalid value in meminfo: %w", err) - } - key := parts[0][:len(parts[0])-1] // remove trailing : from key - // Active(anon) -> Active_anon - key = reParens.ReplaceAllString(key, "_${1}") - switch len(parts) { - case 2: // no unit - case 3: // has unit, we presume kB - fv *= 1024 - key = key + "_bytes" - default: - return nil, fmt.Errorf("invalid line in meminfo: %s", line) - } - memInfo[key] = fv + return nil, fmt.Errorf("Failed to get memory info: %s", err) } - return memInfo, scanner.Err() + metrics := make(map[string]float64) + + if meminfo.ActiveBytes != nil { + metrics["Active_bytes"] = float64(*meminfo.ActiveBytes) + } + if meminfo.ActiveAnonBytes != nil { + metrics["Active_anon_bytes"] = float64(*meminfo.ActiveAnonBytes) + } + if meminfo.ActiveFileBytes != nil { + metrics["Active_file_bytes"] = float64(*meminfo.ActiveFileBytes) + } + if meminfo.AnonHugePagesBytes != nil { + metrics["AnonHugePages_bytes"] = float64(*meminfo.AnonHugePagesBytes) + } + if meminfo.AnonPagesBytes != nil { + metrics["AnonPages_bytes"] = float64(*meminfo.AnonPagesBytes) + } + if meminfo.BounceBytes != nil { + metrics["Bounce_bytes"] = float64(*meminfo.BounceBytes) + } + if meminfo.BuffersBytes != nil { + metrics["Buffers_bytes"] = float64(*meminfo.BuffersBytes) + } + if meminfo.CachedBytes != nil { + metrics["Cached_bytes"] = float64(*meminfo.CachedBytes) + } + if meminfo.CmaFreeBytes != nil { + metrics["CmaFree_bytes"] = float64(*meminfo.CmaFreeBytes) + } + if meminfo.CmaTotalBytes != nil { + metrics["CmaTotal_bytes"] = float64(*meminfo.CmaTotalBytes) + } + if meminfo.CommitLimitBytes != nil { + metrics["CommitLimit_bytes"] = float64(*meminfo.CommitLimitBytes) + } + if meminfo.CommittedASBytes != nil { + metrics["Committed_AS_bytes"] = float64(*meminfo.CommittedASBytes) + } + if meminfo.DirectMap1GBytes != nil { + metrics["DirectMap1G_bytes"] = float64(*meminfo.DirectMap1GBytes) + } + if meminfo.DirectMap2MBytes != nil { + metrics["DirectMap2M_bytes"] = float64(*meminfo.DirectMap2MBytes) + } + if meminfo.DirectMap4kBytes != nil { + metrics["DirectMap4k_bytes"] = float64(*meminfo.DirectMap4kBytes) + } + if meminfo.DirtyBytes != nil { + metrics["Dirty_bytes"] = float64(*meminfo.DirtyBytes) + } + if meminfo.HardwareCorruptedBytes != nil { + metrics["HardwareCorrupted_bytes"] = float64(*meminfo.HardwareCorruptedBytes) + } + if meminfo.HugepagesizeBytes != nil { + metrics["Hugepagesize_bytes"] = float64(*meminfo.HugepagesizeBytes) + } + if meminfo.InactiveBytes != nil { + metrics["Inactive_bytes"] = float64(*meminfo.InactiveBytes) + } + if meminfo.InactiveAnonBytes != nil { + metrics["Inactive_anon_bytes"] = float64(*meminfo.InactiveAnonBytes) + } + if meminfo.InactiveFileBytes != nil { + metrics["Inactive_file_bytes"] = float64(*meminfo.InactiveFileBytes) + } + if meminfo.KernelStackBytes != nil { + metrics["KernelStack_bytes"] = float64(*meminfo.KernelStackBytes) + } + if meminfo.MappedBytes != nil { + metrics["Mapped_bytes"] = float64(*meminfo.MappedBytes) + } + if meminfo.MemAvailableBytes != nil { + metrics["MemAvailable_bytes"] = float64(*meminfo.MemAvailableBytes) + } + if meminfo.MemFreeBytes != nil { + metrics["MemFree_bytes"] = float64(*meminfo.MemFreeBytes) + } + if meminfo.MemTotalBytes != nil { + metrics["MemTotal_bytes"] = float64(*meminfo.MemTotalBytes) + } + if meminfo.MlockedBytes != nil { + metrics["Mlocked_bytes"] = float64(*meminfo.MlockedBytes) + } + if meminfo.NFSUnstableBytes != nil { + metrics["NFS_Unstable_bytes"] = float64(*meminfo.NFSUnstableBytes) + } + if meminfo.PageTablesBytes != nil { + metrics["PageTables_bytes"] = float64(*meminfo.PageTablesBytes) + } + if meminfo.PercpuBytes != nil { + metrics["Percpu_bytes"] = float64(*meminfo.PercpuBytes) + } + if meminfo.SReclaimableBytes != nil { + metrics["SReclaimable_bytes"] = float64(*meminfo.SReclaimableBytes) + } + if meminfo.SUnreclaimBytes != nil { + metrics["SUnreclaim_bytes"] = float64(*meminfo.SUnreclaimBytes) + } + if meminfo.ShmemBytes != nil { + metrics["Shmem_bytes"] = float64(*meminfo.ShmemBytes) + } + if meminfo.ShmemHugePagesBytes != nil { + metrics["ShmemHugePages_bytes"] = float64(*meminfo.ShmemHugePagesBytes) + } + if meminfo.ShmemPmdMappedBytes != nil { + metrics["ShmemPmdMapped_bytes"] = float64(*meminfo.ShmemPmdMappedBytes) + } + if meminfo.SlabBytes != nil { + metrics["Slab_bytes"] = float64(*meminfo.SlabBytes) + } + if meminfo.SwapCachedBytes != nil { + metrics["SwapCached_bytes"] = float64(*meminfo.SwapCachedBytes) + } + if meminfo.SwapFreeBytes != nil { + metrics["SwapFree_bytes"] = float64(*meminfo.SwapFreeBytes) + } + if meminfo.SwapTotalBytes != nil { + metrics["SwapTotal_bytes"] = float64(*meminfo.SwapTotalBytes) + } + if meminfo.UnevictableBytes != nil { + metrics["Unevictable_bytes"] = float64(*meminfo.UnevictableBytes) + } + if meminfo.VmallocChunkBytes != nil { + metrics["VmallocChunk_bytes"] = float64(*meminfo.VmallocChunkBytes) + } + if meminfo.VmallocTotalBytes != nil { + metrics["VmallocTotal_bytes"] = float64(*meminfo.VmallocTotalBytes) + } + if meminfo.VmallocUsedBytes != nil { + metrics["VmallocUsed_bytes"] = float64(*meminfo.VmallocUsedBytes) + } + if meminfo.WritebackBytes != nil { + metrics["Writeback_bytes"] = float64(*meminfo.WritebackBytes) + } + if meminfo.WritebackTmpBytes != nil { + metrics["WritebackTmp_bytes"] = float64(*meminfo.WritebackTmpBytes) + } + + // These fields are always in bytes and do not have `Bytes` + // suffixed counterparts in the procfs.Meminfo struct, nor do + // they have `_bytes` suffix on the metric names. + if meminfo.HugePagesFree != nil { + metrics["HugePages_Free"] = float64(*meminfo.HugePagesFree) + } + if meminfo.HugePagesRsvd != nil { + metrics["HugePages_Rsvd"] = float64(*meminfo.HugePagesRsvd) + } + if meminfo.HugePagesSurp != nil { + metrics["HugePages_Surp"] = float64(*meminfo.HugePagesSurp) + } + if meminfo.HugePagesTotal != nil { + metrics["HugePages_Total"] = float64(*meminfo.HugePagesTotal) + } + + return metrics, nil } diff --git a/collector/meminfo_linux_test.go b/collector/meminfo_linux_test.go index a000bea5..41a0133f 100644 --- a/collector/meminfo_linux_test.go +++ b/collector/meminfo_linux_test.go @@ -17,20 +17,23 @@ package collector import ( - "os" + "io" + "log/slog" "testing" ) func TestMemInfo(t *testing.T) { - file, err := os.Open("fixtures/proc/meminfo") - if err != nil { - t.Fatal(err) - } - defer file.Close() + *procPath = "fixtures/proc" + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) - memInfo, err := parseMemInfo(file) + collector, err := NewMeminfoCollector(logger) if err != nil { - t.Fatal(err) + panic(err) + } + + memInfo, err := collector.(*meminfoCollector).getMemInfo() + if err != nil { + panic(err) } if want, got := 3831959552.0, memInfo["MemTotal_bytes"]; want != got { diff --git a/collector/meminfo_netbsd.go b/collector/meminfo_netbsd.go index a1bd1185..54d23a12 100644 --- a/collector/meminfo_netbsd.go +++ b/collector/meminfo_netbsd.go @@ -18,8 +18,20 @@ package collector import ( "golang.org/x/sys/unix" + "log/slog" ) +type meminfoCollector struct { + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + return &meminfoCollector{ + logger: logger, + }, nil +} + func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { uvmexp, err := unix.SysctlUvmexp("vm.uvmexp2") if err != nil { diff --git a/collector/meminfo_numa_linux.go b/collector/meminfo_numa_linux.go index 5ce08e99..fc26eba4 100644 --- a/collector/meminfo_numa_linux.go +++ b/collector/meminfo_numa_linux.go @@ -20,13 +20,13 @@ import ( "bufio" "fmt" "io" + "log/slog" "os" "path/filepath" "regexp" "strconv" "strings" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -45,7 +45,7 @@ type meminfoMetric struct { type meminfoNumaCollector struct { metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -53,7 +53,7 @@ func init() { } // NewMeminfoNumaCollector returns a new Collector exposing memory stats. -func NewMeminfoNumaCollector(logger log.Logger) (Collector, error) { +func NewMeminfoNumaCollector(logger *slog.Logger) (Collector, error) { return &meminfoNumaCollector{ metricDescs: map[string]*prometheus.Desc{}, logger: logger, diff --git a/collector/meminfo_openbsd.go b/collector/meminfo_openbsd.go index c5d2947e..ca9f46ad 100644 --- a/collector/meminfo_openbsd.go +++ b/collector/meminfo_openbsd.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "log/slog" ) /* @@ -53,6 +54,17 @@ sysctl_bcstats(struct bcachestats *bcstats) */ import "C" +type meminfoCollector struct { + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + return &meminfoCollector{ + logger: logger, + }, nil +} + func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { var uvmexp C.struct_uvmexp var bcstats C.struct_bcachestats diff --git a/collector/meminfo_openbsd_amd64.go b/collector/meminfo_openbsd_amd64.go index 41adebc3..906a36e4 100644 --- a/collector/meminfo_openbsd_amd64.go +++ b/collector/meminfo_openbsd_amd64.go @@ -17,8 +17,10 @@ package collector import ( - "golang.org/x/sys/unix" + "log/slog" "unsafe" + + "golang.org/x/sys/unix" ) const ( @@ -48,6 +50,17 @@ type bcachestats struct { Dmaflips int64 } +type meminfoCollector struct { + logger *slog.Logger +} + +// NewMeminfoCollector returns a new Collector exposing memory stats. +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { + return &meminfoCollector{ + logger: logger, + }, nil +} + func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { uvmexpb, err := unix.SysctlRaw("vm.uvmexp") if err != nil { diff --git a/collector/memory_bsd.go b/collector/memory_bsd.go index 6af9d8aa..1c371052 100644 --- a/collector/memory_bsd.go +++ b/collector/memory_bsd.go @@ -19,8 +19,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -33,7 +33,7 @@ type memoryCollector struct { pageSize uint64 sysctls []bsdSysctl kvm kvm - logger log.Logger + logger *slog.Logger } func init() { @@ -41,7 +41,7 @@ func init() { } // NewMemoryCollector returns a new Collector exposing memory stats. -func NewMemoryCollector(logger log.Logger) (Collector, error) { +func NewMemoryCollector(logger *slog.Logger) (Collector, error) { tmp32, err := unix.SysctlUint32("vm.stats.vm.v_page_size") if err != nil { return nil, fmt.Errorf("sysctl(vm.stats.vm.v_page_size) failed: %w", err) diff --git a/collector/mountstats_linux.go b/collector/mountstats_linux.go index 9dea6fad..03f1a9b6 100644 --- a/collector/mountstats_linux.go +++ b/collector/mountstats_linux.go @@ -18,9 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -96,7 +95,7 @@ type mountStatsCollector struct { proc procfs.Proc - logger log.Logger + logger *slog.Logger } // used to uniquely identify an NFS mount to prevent duplicates @@ -111,7 +110,7 @@ func init() { } // NewMountStatsCollector returns a new Collector exposing NFS statistics. -func NewMountStatsCollector(logger log.Logger) (Collector, error) { +func NewMountStatsCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -538,15 +537,16 @@ func (c *mountStatsCollector) Update(ch chan<- prometheus.Metric) error { mountAddress = miStats.SuperOptions["addr"] } - deviceIdentifier := nfsDeviceIdentifier{m.Device, stats.Transport.Protocol, mountAddress} - i := deviceList[deviceIdentifier] - if i { - level.Debug(c.logger).Log("msg", "Skipping duplicate device entry", "device", deviceIdentifier) - continue + for k := range stats.Transport { + deviceIdentifier := nfsDeviceIdentifier{m.Device, stats.Transport[k].Protocol, mountAddress} + i := deviceList[deviceIdentifier] + if i { + c.logger.Debug("Skipping duplicate device entry", "device", deviceIdentifier) + break + } + deviceList[deviceIdentifier] = true + c.updateNFSStats(ch, stats, m.Device, stats.Transport[k].Protocol, mountAddress) } - - deviceList[deviceIdentifier] = true - c.updateNFSStats(ch, stats, m.Device, stats.Transport.Protocol, mountAddress) } return nil @@ -617,75 +617,77 @@ func (c *mountStatsCollector) updateNFSStats(ch chan<- prometheus.Metric, s *pro labelValues..., ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportBindTotal, - prometheus.CounterValue, - float64(s.Transport.Bind), - labelValues..., - ) + for i := range s.Transport { + ch <- prometheus.MustNewConstMetric( + c.NFSTransportBindTotal, + prometheus.CounterValue, + float64(s.Transport[i].Bind), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportConnectTotal, - prometheus.CounterValue, - float64(s.Transport.Connect), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportConnectTotal, + prometheus.CounterValue, + float64(s.Transport[i].Connect), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportIdleTimeSeconds, - prometheus.GaugeValue, - float64(s.Transport.IdleTimeSeconds%float64Mantissa), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportIdleTimeSeconds, + prometheus.GaugeValue, + float64(s.Transport[i].IdleTimeSeconds%float64Mantissa), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportSendsTotal, - prometheus.CounterValue, - float64(s.Transport.Sends), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportSendsTotal, + prometheus.CounterValue, + float64(s.Transport[i].Sends), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportReceivesTotal, - prometheus.CounterValue, - float64(s.Transport.Receives), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportReceivesTotal, + prometheus.CounterValue, + float64(s.Transport[i].Receives), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportBadTransactionIDsTotal, - prometheus.CounterValue, - float64(s.Transport.BadTransactionIDs), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportBadTransactionIDsTotal, + prometheus.CounterValue, + float64(s.Transport[i].BadTransactionIDs), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportBacklogQueueTotal, - prometheus.CounterValue, - float64(s.Transport.CumulativeBacklog), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportBacklogQueueTotal, + prometheus.CounterValue, + float64(s.Transport[i].CumulativeBacklog), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportMaximumRPCSlots, - prometheus.GaugeValue, - float64(s.Transport.MaximumRPCSlotsUsed), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportMaximumRPCSlots, + prometheus.GaugeValue, + float64(s.Transport[i].MaximumRPCSlotsUsed), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportSendingQueueTotal, - prometheus.CounterValue, - float64(s.Transport.CumulativeSendingQueue), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportSendingQueueTotal, + prometheus.CounterValue, + float64(s.Transport[i].CumulativeSendingQueue), + labelValues..., + ) - ch <- prometheus.MustNewConstMetric( - c.NFSTransportPendingQueueTotal, - prometheus.CounterValue, - float64(s.Transport.CumulativePendingQueue), - labelValues..., - ) + ch <- prometheus.MustNewConstMetric( + c.NFSTransportPendingQueueTotal, + prometheus.CounterValue, + float64(s.Transport[i].CumulativePendingQueue), + labelValues..., + ) + } for _, op := range s.Operations { opLabelValues := []string{export, protocol, mountAddress, op.Operation} diff --git a/collector/netclass_linux.go b/collector/netclass_linux.go index 5d9324cd..87763c3b 100644 --- a/collector/netclass_linux.go +++ b/collector/netclass_linux.go @@ -19,13 +19,13 @@ package collector import ( "errors" "fmt" + "log/slog" "net" "os" "regexp" + "sync" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -41,7 +41,8 @@ type netClassCollector struct { subsystem string ignoredDevicesPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc - logger log.Logger + metricDescsMu sync.Mutex + logger *slog.Logger } func init() { @@ -49,7 +50,7 @@ func init() { } // NewNetClassCollector returns a new Collector exposing network class stats. -func NewNetClassCollector(logger log.Logger) (Collector, error) { +func NewNetClassCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -75,7 +76,7 @@ func (c *netClassCollector) netClassSysfsUpdate(ch chan<- prometheus.Metric) err netClass, err := c.getNetClassInfo() if err != nil { if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "Could not read netclass file", "err", err) + c.logger.Debug("Could not read netclass file", "err", err) return ErrNoData } return fmt.Errorf("could not get net class info: %w", err) @@ -136,6 +137,9 @@ func (c *netClassCollector) netClassSysfsUpdate(ch chan<- prometheus.Metric) err } func (c *netClassCollector) getFieldDesc(name string) *prometheus.Desc { + c.metricDescsMu.Lock() + defer c.metricDescsMu.Unlock() + fieldDesc, exists := c.metricDescs[name] if !exists { diff --git a/collector/netclass_rtnl_linux.go b/collector/netclass_rtnl_linux.go index ef963715..44d89f8f 100644 --- a/collector/netclass_rtnl_linux.go +++ b/collector/netclass_rtnl_linux.go @@ -23,8 +23,7 @@ import ( "path/filepath" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log/level" - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2" "github.com/mdlayher/ethtool" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" @@ -45,7 +44,7 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro if !errors.Is(errors.Unwrap(err), fs.ErrNotExist) { return fmt.Errorf("could not get link modes: %w", err) } - level.Info(c.logger).Log("msg", "ETHTOOL netlink interface unavailable, duplex and linkspeed are not scraped.") + c.logger.Info("ETHTOOL netlink interface unavailable, duplex and linkspeed are not scraped.") } else { for _, lm := range lms { if c.ignoredDevicesPattern.MatchString(lm.Interface.Name) { diff --git a/collector/netdev_aix.go b/collector/netdev_aix.go new file mode 100644 index 00000000..ae316443 --- /dev/null +++ b/collector/netdev_aix.go @@ -0,0 +1,54 @@ +// Copyright 2024 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 !nonetdev +// +build !nonetdev + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" +) + +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { + netDev := netDevStats{} + + stats, err := perfstat.NetAdapterStat() + if err != nil { + return nil, 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), + } + } + + return netDev, nil +} + +func getNetDevLabels() (map[string]map[string]string, error) { + // to be implemented if needed + return nil, nil +} diff --git a/collector/netdev_bsd.go b/collector/netdev_bsd.go index 691bbec4..e1a947e7 100644 --- a/collector/netdev_bsd.go +++ b/collector/netdev_bsd.go @@ -19,9 +19,7 @@ package collector import ( "errors" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "log/slog" ) /* @@ -34,7 +32,7 @@ import ( */ import "C" -func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { netDev := netDevStats{} var ifap, ifa *C.struct_ifaddrs @@ -50,7 +48,7 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error dev := C.GoString(ifa.ifa_name) if filter.ignored(dev) { - level.Debug(logger).Log("msg", "Ignoring device", "device", dev) + logger.Debug("Ignoring device", "device", dev) continue } @@ -72,3 +70,7 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error return netDev, nil } + +func getNetDevLabels() (map[string]map[string]string, error) { + return nil, nil +} diff --git a/collector/netdev_common.go b/collector/netdev_common.go index 089f1e58..c19e5df5 100644 --- a/collector/netdev_common.go +++ b/collector/netdev_common.go @@ -11,22 +11,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nonetdev && (linux || freebsd || openbsd || dragonfly || darwin) +//go:build !nonetdev && (linux || freebsd || openbsd || dragonfly || darwin || aix) // +build !nonetdev -// +build linux freebsd openbsd dragonfly darwin +// +build linux freebsd openbsd dragonfly darwin aix package collector import ( "errors" "fmt" + "log/slog" "net" "strconv" "sync" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -44,7 +43,7 @@ type netDevCollector struct { deviceFilter deviceFilter metricDescsMutex sync.Mutex metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger } type netDevStats map[string]map[string]uint64 @@ -54,10 +53,10 @@ func init() { } // NewNetDevCollector returns a new Collector exposing network device stats. -func NewNetDevCollector(logger log.Logger) (Collector, error) { +func NewNetDevCollector(logger *slog.Logger) (Collector, error) { if *oldNetdevDeviceInclude != "" { if *netdevDeviceInclude == "" { - level.Warn(logger).Log("msg", "--collector.netdev.device-whitelist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-include") + logger.Warn("--collector.netdev.device-whitelist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-include") *netdevDeviceInclude = *oldNetdevDeviceInclude } else { return nil, errors.New("--collector.netdev.device-whitelist and --collector.netdev.device-include are mutually exclusive") @@ -66,7 +65,7 @@ func NewNetDevCollector(logger log.Logger) (Collector, error) { if *oldNetdevDeviceExclude != "" { if *netdevDeviceExclude == "" { - level.Warn(logger).Log("msg", "--collector.netdev.device-blacklist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-exclude") + logger.Warn("--collector.netdev.device-blacklist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-exclude") *netdevDeviceExclude = *oldNetdevDeviceExclude } else { return nil, errors.New("--collector.netdev.device-blacklist and --collector.netdev.device-exclude are mutually exclusive") @@ -78,11 +77,11 @@ func NewNetDevCollector(logger log.Logger) (Collector, error) { } if *netdevDeviceExclude != "" { - level.Info(logger).Log("msg", "Parsed flag --collector.netdev.device-exclude", "flag", *netdevDeviceExclude) + logger.Info("Parsed flag --collector.netdev.device-exclude", "flag", *netdevDeviceExclude) } if *netdevDeviceInclude != "" { - level.Info(logger).Log("msg", "Parsed Flag --collector.netdev.device-include", "flag", *netdevDeviceInclude) + logger.Info("Parsed Flag --collector.netdev.device-include", "flag", *netdevDeviceInclude) } return &netDevCollector{ @@ -93,7 +92,7 @@ func NewNetDevCollector(logger log.Logger) (Collector, error) { }, nil } -func (c *netDevCollector) metricDesc(key string) *prometheus.Desc { +func (c *netDevCollector) metricDesc(key string, labels []string) *prometheus.Desc { c.metricDescsMutex.Lock() defer c.metricDescsMutex.Unlock() @@ -101,7 +100,7 @@ func (c *netDevCollector) metricDesc(key string) *prometheus.Desc { c.metricDescs[key] = prometheus.NewDesc( prometheus.BuildFQName(namespace, c.subsystem, key+"_total"), fmt.Sprintf("Network device statistic %s.", key), - []string{"device"}, + labels, nil, ) } @@ -114,13 +113,29 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get netstats: %w", err) } + + netDevLabels, err := getNetDevLabels() + if err != nil { + return fmt.Errorf("couldn't get netdev labels: %w", err) + } + for dev, devStats := range netDev { if !*netdevDetailedMetrics { legacy(devStats) } + + labels := []string{"device"} + labelValues := []string{dev} + if devLabels, exists := netDevLabels[dev]; exists { + for labelName, labelValue := range devLabels { + labels = append(labels, labelName) + labelValues = append(labelValues, labelValue) + } + } + for key, value := range devStats { - desc := c.metricDesc(key) - ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(value), dev) + desc := c.metricDesc(key, labels) + ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(value), labelValues...) } } if *netdevAddressInfo { diff --git a/collector/netdev_darwin.go b/collector/netdev_darwin.go index c08f1f8e..2367ebe5 100644 --- a/collector/netdev_darwin.go +++ b/collector/netdev_darwin.go @@ -20,14 +20,13 @@ import ( "bytes" "encoding/binary" "fmt" + "log/slog" "net" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "golang.org/x/sys/unix" ) -func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { netDev := netDevStats{} ifs, err := net.Interfaces() @@ -37,13 +36,13 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error for _, iface := range ifs { if filter.ignored(iface.Name) { - level.Debug(logger).Log("msg", "Ignoring device", "device", iface.Name) + logger.Debug("Ignoring device", "device", iface.Name) continue } ifaceData, err := getIfaceData(iface.Index) if err != nil { - level.Debug(logger).Log("msg", "failed to load data for interface", "device", iface.Name, "err", err) + logger.Debug("failed to load data for interface", "device", iface.Name, "err", err) continue } @@ -118,3 +117,8 @@ type ifData64 struct { Xmittiming uint32 Lastchange unix.Timeval32 } + +func getNetDevLabels() (map[string]map[string]string, error) { + // to be implemented if needed + return nil, nil +} diff --git a/collector/netdev_linux.go b/collector/netdev_linux.go index f3348cda..58ac4af5 100644 --- a/collector/netdev_linux.go +++ b/collector/netdev_linux.go @@ -18,26 +18,27 @@ package collector import ( "fmt" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2" "github.com/prometheus/procfs" + "github.com/prometheus/procfs/sysfs" ) var ( - netDevNetlink = kingpin.Flag("collector.netdev.netlink", "Use netlink to gather stats instead of /proc/net/dev.").Default("true").Bool() + netDevNetlink = kingpin.Flag("collector.netdev.netlink", "Use netlink to gather stats instead of /proc/net/dev.").Default("true").Bool() + netdevLabelIfAlias = kingpin.Flag("collector.netdev.label-ifalias", "Add ifAlias label").Default("false").Bool() ) -func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { if *netDevNetlink { return netlinkStats(filter, logger) } return procNetDevStats(filter, logger) } -func netlinkStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func netlinkStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { conn, err := rtnetlink.Dial(nil) if err != nil { return nil, err @@ -52,12 +53,12 @@ func netlinkStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) return parseNetlinkStats(links, filter, logger), nil } -func parseNetlinkStats(links []rtnetlink.LinkMessage, filter *deviceFilter, logger log.Logger) netDevStats { +func parseNetlinkStats(links []rtnetlink.LinkMessage, filter *deviceFilter, logger *slog.Logger) netDevStats { metrics := netDevStats{} for _, msg := range links { if msg.Attributes == nil { - level.Debug(logger).Log("msg", "No netlink attributes, skipping") + logger.Debug("No netlink attributes, skipping") continue } name := msg.Attributes.Name @@ -93,13 +94,13 @@ func parseNetlinkStats(links []rtnetlink.LinkMessage, filter *deviceFilter, logg } if filter.ignored(name) { - level.Debug(logger).Log("msg", "Ignoring device", "device", name) + logger.Debug("Ignoring device", "device", name) continue } // Make sure we don't panic when accessing `stats` attributes below. if stats == nil { - level.Debug(logger).Log("msg", "No netlink stats, skipping") + logger.Debug("No netlink stats, skipping") continue } @@ -141,7 +142,7 @@ func parseNetlinkStats(links []rtnetlink.LinkMessage, filter *deviceFilter, logg return metrics } -func procNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func procNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { metrics := netDevStats{} fs, err := procfs.NewFS(*procPath) @@ -158,7 +159,7 @@ func procNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, erro name := stats.Name if filter.ignored(name) { - level.Debug(logger).Log("msg", "Ignoring device", "device", name) + logger.Debug("Ignoring device", "device", name) continue } @@ -184,3 +185,26 @@ func procNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, erro return metrics, nil } + +func getNetDevLabels() (map[string]map[string]string, error) { + if !*netdevLabelIfAlias { + return nil, nil + } + + fs, err := sysfs.NewFS(*sysPath) + if err != nil { + return nil, err + } + + interfaces, err := fs.NetClass() + if err != nil { + return nil, err + } + + labels := make(map[string]map[string]string) + for iface, params := range interfaces { + labels[iface] = map[string]string{"ifalias": params.IfAlias} + } + + return labels, nil +} diff --git a/collector/netdev_linux_test.go b/collector/netdev_linux_test.go index 7909d018..06700fd8 100644 --- a/collector/netdev_linux_test.go +++ b/collector/netdev_linux_test.go @@ -17,11 +17,11 @@ package collector import ( + "io" + "log/slog" "testing" - "github.com/go-kit/log" - - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2" ) var links = []rtnetlink.LinkMessage{ @@ -166,7 +166,7 @@ var links = []rtnetlink.LinkMessage{ func TestNetDevStatsIgnore(t *testing.T) { filter := newDeviceFilter("^veth", "") - netStats := parseNetlinkStats(links, &filter, log.NewNopLogger()) + netStats := parseNetlinkStats(links, &filter, slog.New(slog.NewTextHandler(io.Discard, nil))) if want, got := uint64(10437182923), netStats["wlan0"]["receive_bytes"]; want != got { t.Errorf("want netstat wlan0 bytes %v, got %v", want, got) @@ -199,7 +199,7 @@ func TestNetDevStatsIgnore(t *testing.T) { func TestNetDevStatsAccept(t *testing.T) { filter := newDeviceFilter("", "^💩0$") - netStats := parseNetlinkStats(links, &filter, log.NewNopLogger()) + netStats := parseNetlinkStats(links, &filter, slog.New(slog.NewTextHandler(io.Discard, nil))) if want, got := 1, len(netStats); want != got { t.Errorf("want count of devices to be %d, got %d", want, got) @@ -230,7 +230,7 @@ func TestNetDevLegacyMetricNames(t *testing.T) { } filter := newDeviceFilter("", "") - netStats := parseNetlinkStats(links, &filter, log.NewNopLogger()) + netStats := parseNetlinkStats(links, &filter, slog.New(slog.NewTextHandler(io.Discard, nil))) for dev, devStats := range netStats { legacy(devStats) @@ -263,7 +263,7 @@ func TestNetDevLegacyMetricValues(t *testing.T) { } filter := newDeviceFilter("", "^enp0s0f0$") - netStats := parseNetlinkStats(links, &filter, log.NewNopLogger()) + netStats := parseNetlinkStats(links, &filter, slog.New(slog.NewTextHandler(io.Discard, nil))) metrics, ok := netStats["enp0s0f0"] if !ok { t.Error("expected stats for interface enp0s0f0") @@ -285,7 +285,7 @@ func TestNetDevLegacyMetricValues(t *testing.T) { func TestNetDevMetricValues(t *testing.T) { filter := newDeviceFilter("", "") - netStats := parseNetlinkStats(links, &filter, log.NewNopLogger()) + netStats := parseNetlinkStats(links, &filter, slog.New(slog.NewTextHandler(io.Discard, nil))) for _, msg := range links { device := msg.Attributes.Name diff --git a/collector/netdev_openbsd.go b/collector/netdev_openbsd.go index b90e3ba7..31c2640b 100644 --- a/collector/netdev_openbsd.go +++ b/collector/netdev_openbsd.go @@ -18,9 +18,7 @@ package collector import ( "errors" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "log/slog" ) /* @@ -31,7 +29,7 @@ import ( */ import "C" -func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { netDev := netDevStats{} var ifap, ifa *C.struct_ifaddrs @@ -47,7 +45,7 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error dev := C.GoString(ifa.ifa_name) if filter.ignored(dev) { - level.Debug(logger).Log("msg", "Ignoring device", "device", dev) + logger.Debug("Ignoring device", "device", dev) continue } @@ -72,3 +70,8 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error return netDev, nil } + +func getNetDevLabels() (map[string]map[string]string, error) { + // to be implemented if needed + return nil, nil +} diff --git a/collector/netdev_openbsd_amd64.go b/collector/netdev_openbsd_amd64.go index da8a81f3..e9857164 100644 --- a/collector/netdev_openbsd_amd64.go +++ b/collector/netdev_openbsd_amd64.go @@ -17,14 +17,14 @@ package collector import ( - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "log/slog" + + "unsafe" "golang.org/x/sys/unix" - "unsafe" ) -func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error) { +func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, error) { netDev := netDevStats{} mib := [6]_C_int{unix.CTL_NET, unix.AF_ROUTE, 0, 0, unix.NET_RT_IFLIST, 0} @@ -54,7 +54,7 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error data := ifm.Data dev := int8ToString(dl.Data[:dl.Nlen]) if filter.ignored(dev) { - level.Debug(logger).Log("msg", "Ignoring device", "device", dev) + logger.Debug("Ignoring device", "device", dev) continue } @@ -76,3 +76,8 @@ func getNetDevStats(filter *deviceFilter, logger log.Logger) (netDevStats, error } return netDev, nil } + +func getNetDevLabels() (map[string]map[string]string, error) { + // to be implemented if needed + return nil, nil +} diff --git a/collector/netisr_freebsd.go b/collector/netisr_freebsd.go index 442bcdc6..b3a4451c 100644 --- a/collector/netisr_freebsd.go +++ b/collector/netisr_freebsd.go @@ -18,14 +18,14 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) type netisrCollector struct { sysctls []bsdSysctl - logger log.Logger + logger *slog.Logger } const ( @@ -36,7 +36,7 @@ func init() { registerCollector("netisr", defaultEnabled, NewNetisrCollector) } -func NewNetisrCollector(logger log.Logger) (Collector, error) { +func NewNetisrCollector(logger *slog.Logger) (Collector, error) { return &netisrCollector{ sysctls: []bsdSysctl{ { diff --git a/collector/netstat_linux.go b/collector/netstat_linux.go index 8e4c9f4d..1aa92340 100644 --- a/collector/netstat_linux.go +++ b/collector/netstat_linux.go @@ -21,13 +21,13 @@ import ( "errors" "fmt" "io" + "log/slog" "os" "regexp" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -36,12 +36,12 @@ const ( ) var ( - netStatFields = kingpin.Flag("collector.netstat.fields", "Regexp of fields to return for netstat collector.").Default("^(.*_(InErrors|InErrs)|Ip_Forwarding|Ip(6|Ext)_(InOctets|OutOctets)|Icmp6?_(InMsgs|OutMsgs)|TcpExt_(Listen.*|Syncookies.*|TCPSynRetrans|TCPTimeouts|TCPOFOQueue)|Tcp_(ActiveOpens|InSegs|OutSegs|OutRsts|PassiveOpens|RetransSegs|CurrEstab)|Udp6?_(InDatagrams|OutDatagrams|NoPorts|RcvbufErrors|SndbufErrors))$").String() + netStatFields = kingpin.Flag("collector.netstat.fields", "Regexp of fields to return for netstat collector.").Default("^(.*_(InErrors|InErrs)|Ip_Forwarding|Ip(6|Ext)_(InOctets|OutOctets)|Icmp6?_(InMsgs|OutMsgs)|TcpExt_(Listen.*|Syncookies.*|TCPSynRetrans|TCPTimeouts|TCPOFOQueue|TCPRcvQDrop)|Tcp_(ActiveOpens|InSegs|OutSegs|OutRsts|PassiveOpens|RetransSegs|CurrEstab)|Udp6?_(InDatagrams|OutDatagrams|NoPorts|RcvbufErrors|SndbufErrors))$").String() ) type netStatCollector struct { fieldPattern *regexp.Regexp - logger log.Logger + logger *slog.Logger } func init() { @@ -50,7 +50,7 @@ func init() { // NewNetStatCollector takes and returns // a new Collector exposing network stats. -func NewNetStatCollector(logger log.Logger) (Collector, error) { +func NewNetStatCollector(logger *slog.Logger) (Collector, error) { pattern := regexp.MustCompile(*netStatFields) return &netStatCollector{ fieldPattern: pattern, diff --git a/collector/network_route_linux.go b/collector/network_route_linux.go index c77e1751..5a5c34d2 100644 --- a/collector/network_route_linux.go +++ b/collector/network_route_linux.go @@ -18,19 +18,19 @@ package collector import ( "fmt" - "golang.org/x/sys/unix" + "log/slog" "net" "strconv" - "github.com/go-kit/log" - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2" "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" ) type networkRouteCollector struct { routeInfoDesc *prometheus.Desc routesDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -38,7 +38,7 @@ func init() { } // NewNetworkRouteCollector returns a new Collector exposing systemd statistics. -func NewNetworkRouteCollector(logger log.Logger) (Collector, error) { +func NewNetworkRouteCollector(logger *slog.Logger) (Collector, error) { const subsystem = "network" routeInfoDesc := prometheus.NewDesc( diff --git a/collector/nfs_linux.go b/collector/nfs_linux.go index e08acdba..ea2a1b52 100644 --- a/collector/nfs_linux.go +++ b/collector/nfs_linux.go @@ -19,11 +19,10 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "reflect" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/nfs" ) @@ -40,7 +39,7 @@ type nfsCollector struct { nfsRPCRetransmissionsDesc *prometheus.Desc nfsRPCAuthenticationRefreshesDesc *prometheus.Desc nfsProceduresDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -48,7 +47,7 @@ func init() { } // NewNfsCollector returns a new Collector exposing NFS statistics. -func NewNfsCollector(logger log.Logger) (Collector, error) { +func NewNfsCollector(logger *slog.Logger) (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -100,7 +99,7 @@ func (c *nfsCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.ClientRPCStats() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "Not collecting NFS metrics", "err", err) + c.logger.Debug("Not collecting NFS metrics", "err", err) return ErrNoData } return fmt.Errorf("failed to retrieve nfs stats: %w", err) diff --git a/collector/nfsd_linux.go b/collector/nfsd_linux.go index 8b310ea2..7f1bc240 100644 --- a/collector/nfsd_linux.go +++ b/collector/nfsd_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/nfs" ) @@ -32,7 +31,7 @@ import ( type nfsdCollector struct { fs nfs.FS requestsDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -44,7 +43,7 @@ const ( ) // NewNFSdCollector returns a new Collector exposing /proc/net/rpc/nfsd statistics. -func NewNFSdCollector(logger log.Logger) (Collector, error) { +func NewNFSdCollector(logger *slog.Logger) (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -66,7 +65,7 @@ func (c *nfsdCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.ServerRPCStats() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "Not collecting NFSd metrics", "err", err) + c.logger.Debug("Not collecting NFSd metrics", "err", err) return ErrNoData } return fmt.Errorf("failed to retrieve nfsd stats: %w", err) diff --git a/collector/ntp.go b/collector/ntp.go index 8b8db8a9..10e639b1 100644 --- a/collector/ntp.go +++ b/collector/ntp.go @@ -18,14 +18,13 @@ package collector import ( "fmt" + "log/slog" "net" "sync" "time" "github.com/alecthomas/kingpin/v2" "github.com/beevik/ntp" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -51,7 +50,7 @@ var ( type ntpCollector struct { stratum, leap, rtt, offset, reftime, rootDelay, rootDispersion, sanity typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -62,7 +61,7 @@ func init() { // Default definition of "local" is: // - collector.ntp.server address is a loopback address (or collector.ntp.server-is-mine flag is turned on) // - the server is reachable with outgoin IP_TTL = 1 -func NewNtpCollector(logger log.Logger) (Collector, error) { +func NewNtpCollector(logger *slog.Logger) (Collector, error) { ipaddr := net.ParseIP(*ntpServer) if !*ntpServerIsLocal && (ipaddr == nil || !ipaddr.IsLoopback()) { return nil, fmt.Errorf("only IP address of local NTP server is valid for --collector.ntp.server") @@ -80,7 +79,7 @@ func NewNtpCollector(logger log.Logger) (Collector, error) { return nil, fmt.Errorf("invalid NTP port number %d; must be between 1 and 65535 inclusive", *ntpServerPort) } - level.Warn(logger).Log("msg", "This collector is deprecated and will be removed in the next major version release.") + logger.Warn("This collector is deprecated and will be removed in the next major version release.") return &ntpCollector{ stratum: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, ntpSubsystem, "stratum"), diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index 81d4ab29..d1a9a87b 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -19,17 +19,16 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) type nvmeCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -37,7 +36,7 @@ func init() { } // NewNVMeCollector returns a new Collector exposing NVMe stats. -func NewNVMeCollector(logger log.Logger) (Collector, error) { +func NewNVMeCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -53,7 +52,7 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { devices, err := c.fs.NVMeClass() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "nvme statistics not found, skipping") + c.logger.Debug("nvme statistics not found, skipping") return ErrNoData } return fmt.Errorf("error obtaining NVMe class info: %w", err) diff --git a/collector/os_release.go b/collector/os_release.go index 14a025a4..e589c4ff 100644 --- a/collector/os_release.go +++ b/collector/os_release.go @@ -11,12 +11,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !noosrelease && !aix +// +build !noosrelease,!aix + package collector import ( "encoding/xml" "errors" "io" + "log/slog" "os" "regexp" "strconv" @@ -24,8 +28,6 @@ import ( "sync" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" envparse "github.com/hashicorp/go-envparse" "github.com/prometheus/client_golang/prometheus" ) @@ -53,18 +55,19 @@ type osRelease struct { BuildID string ImageID string ImageVersion string + SupportEnd string } type osReleaseCollector struct { infoDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger os *osRelease - osFilename string // file name of cached release information - osMtime time.Time // mtime of cached release file osMutex sync.RWMutex osReleaseFilenames []string // all os-release file names to check version float64 versionDesc *prometheus.Desc + supportEnd time.Time + supportEndDesc *prometheus.Desc } type Plist struct { @@ -81,7 +84,7 @@ func init() { } // NewOSCollector returns a new Collector exposing os-release information. -func NewOSCollector(logger log.Logger) (Collector, error) { +func NewOSCollector(logger *slog.Logger) (Collector, error) { return &osReleaseCollector{ logger: logger, infoDesc: prometheus.NewDesc( @@ -97,6 +100,11 @@ func NewOSCollector(logger log.Logger) (Collector, error) { "Metric containing the major.minor part of the OS version.", []string{"id", "id_like", "name"}, nil, ), + supportEndDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "os", "support_end_timestamp_seconds"), + "Metric containing the end-of-life date timestamp of the OS.", + nil, nil, + ), }, nil } @@ -115,6 +123,7 @@ func parseOSRelease(r io.Reader) (*osRelease, error) { BuildID: env["BUILD_ID"], ImageID: env["IMAGE_ID"], ImageVersion: env["IMAGE_VERSION"], + SupportEnd: env["SUPPORT_END"], }, err } @@ -125,28 +134,10 @@ func (c *osReleaseCollector) UpdateStruct(path string) error { } defer releaseFile.Close() - stat, err := releaseFile.Stat() - if err != nil { - return err - } - - t := stat.ModTime() - c.osMutex.RLock() - upToDate := path == c.osFilename && t == c.osMtime - c.osMutex.RUnlock() - if upToDate { - // osReleaseCollector struct is already up-to-date. - return nil - } - // Acquire a lock to update the osReleaseCollector struct. c.osMutex.Lock() defer c.osMutex.Unlock() - level.Debug(c.logger).Log("msg", "file modification time has changed", - "file", path, "old_value", c.osMtime, "new_value", t) - c.osFilename = path - c.osMtime = t // SystemVersion.plist is xml file with MacOs version info if strings.Contains(releaseFile.Name(), "SystemVersion.plist") { c.os, err = getMacosProductVersion(releaseFile.Name()) @@ -169,6 +160,15 @@ func (c *osReleaseCollector) UpdateStruct(path string) error { } else { c.version = 0 } + + if c.os.SupportEnd != "" { + c.supportEnd, err = time.Parse(time.DateOnly, c.os.SupportEnd) + + if err != nil { + return err + } + } + return nil } @@ -180,7 +180,7 @@ func (c *osReleaseCollector) Update(ch chan<- prometheus.Metric) error { } if errors.Is(err, os.ErrNotExist) { if i >= (len(c.osReleaseFilenames) - 1) { - level.Debug(c.logger).Log("msg", "no os-release file found", "files", strings.Join(c.osReleaseFilenames, ",")) + c.logger.Debug("no os-release file found", "files", strings.Join(c.osReleaseFilenames, ",")) return ErrNoData } continue @@ -195,6 +195,11 @@ func (c *osReleaseCollector) Update(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric(c.versionDesc, prometheus.GaugeValue, c.version, c.os.ID, c.os.IDLike, c.os.Name) } + + if c.os.SupportEnd != "" { + ch <- prometheus.MustNewConstMetric(c.supportEndDesc, prometheus.GaugeValue, float64(c.supportEnd.Unix())) + } + return nil } diff --git a/collector/os_release_test.go b/collector/os_release_test.go index 46838f15..e5d6b501 100644 --- a/collector/os_release_test.go +++ b/collector/os_release_test.go @@ -14,12 +14,12 @@ package collector import ( + "io" + "log/slog" "os" "reflect" "strings" "testing" - - "github.com/go-kit/log" ) const debianBullseye string = `PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" @@ -33,12 +33,28 @@ SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" ` +const nixosTapir string = `BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues" +BUILD_ID="23.11.20240328.219951b" +DOCUMENTATION_URL="https://nixos.org/learn.html" +HOME_URL="https://nixos.org/" +ID=nixos +LOGO="nix-snowflake" +NAME=NixOS +PRETTY_NAME="NixOS 23.11 (Tapir)" +SUPPORT_END="2024-06-30" +SUPPORT_URL="https://nixos.org/community.html" +VERSION="23.11 (Tapir)" +VERSION_CODENAME=tapir +VERSION_ID="23.11" +` + func TestParseOSRelease(t *testing.T) { want := &osRelease{ Name: "Ubuntu", ID: "ubuntu", IDLike: "debian", PrettyName: "Ubuntu 20.04.2 LTS", + SupportEnd: "", Version: "20.04.2 LTS (Focal Fossa)", VersionID: "20.04", VersionCodename: "focal", @@ -75,6 +91,32 @@ func TestParseOSRelease(t *testing.T) { } } +func TestParseOSSupportEnd(t *testing.T) { + want := &osRelease{ + BuildID: "23.11.20240328.219951b", + Name: "NixOS", + ID: "nixos", + IDLike: "", + ImageID: "", + ImageVersion: "", + PrettyName: "NixOS 23.11 (Tapir)", + SupportEnd: "2024-06-30", + Variant: "", + VariantID: "", + Version: "23.11 (Tapir)", + VersionID: "23.11", + VersionCodename: "tapir", + } + + got, err := parseOSRelease(strings.NewReader(nixosTapir)) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(want, got) { + t.Fatalf("should have %+v osRelease: got %+v", want, got) + } +} + func TestUpdateStruct(t *testing.T) { wantedOS := &osRelease{ Name: "Ubuntu", @@ -87,7 +129,7 @@ func TestUpdateStruct(t *testing.T) { } wantedVersion := 20.04 - collector, err := NewOSCollector(log.NewNopLogger()) + collector, err := NewOSCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Fatal(err) } diff --git a/collector/perf_linux.go b/collector/perf_linux.go index 8934371a..b62d9b80 100644 --- a/collector/perf_linux.go +++ b/collector/perf_linux.go @@ -18,13 +18,12 @@ package collector import ( "fmt" + "log/slog" "runtime" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/hodgesds/perf-utils" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" @@ -189,7 +188,7 @@ type perfCollector struct { perfSwProfilers map[int]*perf.SoftwareProfiler perfCacheProfilers map[int]*perf.CacheProfiler desc map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger tracepointCollector *perfTracepointCollector } @@ -199,7 +198,7 @@ type perfTracepointCollector struct { // collection order is the sorted configured collection order of the profiler. collectionOrder []string - logger log.Logger + logger *slog.Logger profilers map[int]perf.GroupProfiler } @@ -218,7 +217,7 @@ func (c *perfTracepointCollector) updateCPU(cpu int, ch chan<- prometheus.Metric profiler := c.profilers[cpu] p := &perf.GroupProfileValue{} if err := profiler.Profile(p); err != nil { - level.Error(c.logger).Log("msg", "Failed to collect tracepoint profile", "err", err) + c.logger.Error("Failed to collect tracepoint profile", "err", err) return err } @@ -240,7 +239,7 @@ func (c *perfTracepointCollector) updateCPU(cpu int, ch chan<- prometheus.Metric // newPerfTracepointCollector returns a configured perfTracepointCollector. func newPerfTracepointCollector( - logger log.Logger, + logger *slog.Logger, tracepointsFlag []string, cpus []int, ) (*perfTracepointCollector, error) { @@ -301,7 +300,7 @@ func newPerfTracepointCollector( // NewPerfCollector returns a new perf based collector, it creates a profiler // per CPU. -func NewPerfCollector(logger log.Logger) (Collector, error) { +func NewPerfCollector(logger *slog.Logger) (Collector, error) { collector := &perfCollector{ perfHwProfilers: map[int]*perf.HardwareProfiler{}, perfSwProfilers: map[int]*perf.SoftwareProfiler{}, diff --git a/collector/perf_linux_test.go b/collector/perf_linux_test.go index fc557ffd..e3858511 100644 --- a/collector/perf_linux_test.go +++ b/collector/perf_linux_test.go @@ -17,13 +17,14 @@ package collector import ( + "io" + "log/slog" "os" "runtime" "strconv" "strings" "testing" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -44,7 +45,7 @@ func canTestPerf(t *testing.T) { func TestPerfCollector(t *testing.T) { canTestPerf(t) - collector, err := NewPerfCollector(log.NewNopLogger()) + collector, err := NewPerfCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Fatal(err) } @@ -97,7 +98,7 @@ func TestPerfCollectorStride(t *testing.T) { } } perfCPUsFlag = &test.flag - collector, err := NewPerfCollector(log.NewNopLogger()) + collector, err := NewPerfCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) if err != nil { t.Fatal(err) } diff --git a/collector/powersupplyclass.go b/collector/powersupplyclass.go index 7f231dac..a5e648c0 100644 --- a/collector/powersupplyclass.go +++ b/collector/powersupplyclass.go @@ -18,10 +18,10 @@ package collector import ( + "log/slog" "regexp" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -33,14 +33,14 @@ type powerSupplyClassCollector struct { subsystem string ignoredPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { registerCollector("powersupplyclass", defaultEnabled, NewPowerSupplyClassCollector) } -func NewPowerSupplyClassCollector(logger log.Logger) (Collector, error) { +func NewPowerSupplyClassCollector(logger *slog.Logger) (Collector, error) { pattern := regexp.MustCompile(*powerSupplyClassIgnoredPowerSupplies) return &powerSupplyClassCollector{ subsystem: "power_supply", diff --git a/collector/pressure_linux.go b/collector/pressure_linux.go index ceaced7e..7338b7e6 100644 --- a/collector/pressure_linux.go +++ b/collector/pressure_linux.go @@ -19,17 +19,16 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "syscall" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) var ( - psiResources = []string{"cpu", "io", "memory"} + psiResources = []string{"cpu", "io", "memory", "irq"} ) type pressureStatsCollector struct { @@ -38,10 +37,11 @@ type pressureStatsCollector struct { ioFull *prometheus.Desc mem *prometheus.Desc memFull *prometheus.Desc + irqFull *prometheus.Desc fs procfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -49,7 +49,7 @@ func init() { } // NewPressureStatsCollector returns a Collector exposing pressure stall information -func NewPressureStatsCollector(logger log.Logger) (Collector, error) { +func NewPressureStatsCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -81,6 +81,11 @@ func NewPressureStatsCollector(logger log.Logger) (Collector, error) { "Total time in seconds no process could make progress due to memory congestion", nil, nil, ), + irqFull: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "pressure", "irq_stalled_seconds_total"), + "Total time in seconds no process could make progress due to IRQ congestion", + nil, nil, + ), fs: fs, logger: logger, }, nil @@ -89,19 +94,29 @@ func NewPressureStatsCollector(logger log.Logger) (Collector, error) { // Update calls procfs.NewPSIStatsForResource for the different resources and updates the values func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { for _, res := range psiResources { - level.Debug(c.logger).Log("msg", "collecting statistics for resource", "resource", res) + c.logger.Debug("collecting statistics for resource", "resource", res) vals, err := c.fs.PSIStatsForResource(res) if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel") + c.logger.Debug("pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel") return ErrNoData } if errors.Is(err, syscall.ENOTSUP) { - level.Debug(c.logger).Log("msg", "pressure information is disabled, add psi=1 kernel command line to enable it") + c.logger.Debug("pressure information is disabled, add psi=1 kernel command line to enable it") return ErrNoData } return fmt.Errorf("failed to retrieve pressure stats: %w", err) } + // IRQ pressure does not have 'some' data. + // See https://github.com/torvalds/linux/blob/v6.9/include/linux/psi_types.h#L65 + if vals.Some == nil && res != "irq" { + c.logger.Debug("pressure information returned no 'some' data") + return ErrNoData + } + if vals.Full == nil && res != "cpu" { + c.logger.Debug("pressure information returned no 'full' data") + return ErrNoData + } switch res { case "cpu": ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) @@ -111,8 +126,10 @@ func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { case "memory": ch <- prometheus.MustNewConstMetric(c.mem, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.memFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) + case "irq": + ch <- prometheus.MustNewConstMetric(c.irqFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) default: - level.Debug(c.logger).Log("msg", "did not account for resource", "resource", res) + c.logger.Debug("did not account for resource", "resource", res) } } diff --git a/collector/processes_linux.go b/collector/processes_linux.go index 798aeaeb..653045e5 100644 --- a/collector/processes_linux.go +++ b/collector/processes_linux.go @@ -19,14 +19,13 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "path" "strconv" "strings" "syscall" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -39,7 +38,7 @@ type processCollector struct { procsState *prometheus.Desc pidUsed *prometheus.Desc pidMax *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -47,7 +46,7 @@ func init() { } // NewProcessStatCollector returns a new Collector exposing process data read from the proc filesystem. -func NewProcessStatCollector(logger log.Logger) (Collector, error) { +func NewProcessStatCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -130,10 +129,10 @@ func (c *processCollector) getAllocatedThreads() (int, map[string]int32, int, ma if err != nil { // PIDs can vanish between getting the list and getting stats. if c.isIgnoredError(err) { - level.Debug(c.logger).Log("msg", "file not found when retrieving stats for pid", "pid", pid.PID, "err", err) + c.logger.Debug("file not found when retrieving stats for pid", "pid", pid.PID, "err", err) continue } - level.Debug(c.logger).Log("msg", "error reading stat for pid", "pid", pid.PID, "err", err) + c.logger.Debug("error reading stat for pid", "pid", pid.PID, "err", err) return 0, nil, 0, nil, fmt.Errorf("error reading stat for pid %d: %w", pid.PID, err) } pids++ @@ -151,17 +150,17 @@ func (c *processCollector) getThreadStates(pid int, pidStat procfs.ProcStat, thr fs, err := procfs.NewFS(procFilePath(path.Join(strconv.Itoa(pid), "task"))) if err != nil { if c.isIgnoredError(err) { - level.Debug(c.logger).Log("msg", "file not found when retrieving tasks for pid", "pid", pid, "err", err) + c.logger.Debug("file not found when retrieving tasks for pid", "pid", pid, "err", err) return nil } - level.Debug(c.logger).Log("msg", "error reading tasks for pid", "pid", pid, "err", err) + c.logger.Debug("error reading tasks for pid", "pid", pid, "err", err) return fmt.Errorf("error reading task for pid %d: %w", pid, err) } t, err := fs.AllProcs() if err != nil { if c.isIgnoredError(err) { - level.Debug(c.logger).Log("msg", "file not found when retrieving tasks for pid", "pid", pid, "err", err) + c.logger.Debug("file not found when retrieving tasks for pid", "pid", pid, "err", err) return nil } return fmt.Errorf("unable to list all threads for pid: %d %w", pid, err) @@ -175,10 +174,10 @@ func (c *processCollector) getThreadStates(pid int, pidStat procfs.ProcStat, thr threadStat, err := thread.Stat() if err != nil { if c.isIgnoredError(err) { - level.Debug(c.logger).Log("msg", "file not found when retrieving stats for thread", "pid", pid, "threadId", thread.PID, "err", err) + c.logger.Debug("file not found when retrieving stats for thread", "pid", pid, "threadId", thread.PID, "err", err) continue } - level.Debug(c.logger).Log("msg", "error reading stat for thread", "pid", pid, "threadId", thread.PID, "err", err) + c.logger.Debug("error reading stat for thread", "pid", pid, "threadId", thread.PID, "err", err) return fmt.Errorf("error reading stat for pid:%d thread:%d err:%w", pid, thread.PID, err) } threadStates[threadStat.State]++ diff --git a/collector/processes_linux_test.go b/collector/processes_linux_test.go index e2814a01..c50d16c8 100644 --- a/collector/processes_linux_test.go +++ b/collector/processes_linux_test.go @@ -17,10 +17,11 @@ package collector import ( + "io" + "log/slog" "testing" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/procfs" ) @@ -33,7 +34,7 @@ func TestReadProcessStatus(t *testing.T) { if err != nil { t.Errorf("failed to open procfs: %v", err) } - c := processCollector{fs: fs, logger: log.NewNopLogger()} + c := processCollector{fs: fs, logger: slog.New(slog.NewTextHandler(io.Discard, nil))} pids, states, threads, _, err := c.getAllocatedThreads() if err != nil { t.Fatalf("Cannot retrieve data from procfs getAllocatedThreads function: %v ", err) diff --git a/collector/qdisc_linux.go b/collector/qdisc_linux.go index 06ab6a88..93a78e3b 100644 --- a/collector/qdisc_linux.go +++ b/collector/qdisc_linux.go @@ -19,18 +19,17 @@ package collector import ( "encoding/json" "fmt" + "log/slog" "os" "path/filepath" "github.com/alecthomas/kingpin/v2" "github.com/ema/qdisc" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) type qdiscStatCollector struct { - logger log.Logger + logger *slog.Logger deviceFilter deviceFilter bytes typedDesc packets typedDesc @@ -54,10 +53,10 @@ func init() { } // NewQdiscStatCollector returns a new Collector exposing queuing discipline statistics. -func NewQdiscStatCollector(logger log.Logger) (Collector, error) { +func NewQdiscStatCollector(logger *slog.Logger) (Collector, error) { if *oldCollectorQdiskDeviceInclude != "" { if *collectorQdiscDeviceInclude == "" { - level.Warn(logger).Log("msg", "--collector.qdisk.device-include is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-include") + logger.Warn("--collector.qdisk.device-include is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-include") *collectorQdiscDeviceInclude = *oldCollectorQdiskDeviceInclude } else { return nil, fmt.Errorf("--collector.qdisk.device-include and --collector.qdisc.device-include are mutually exclusive") @@ -66,7 +65,7 @@ func NewQdiscStatCollector(logger log.Logger) (Collector, error) { if *oldCollectorQdiskDeviceExclude != "" { if *collectorQdiscDeviceExclude == "" { - level.Warn(logger).Log("msg", "--collector.qdisk.device-exclude is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-exclude") + logger.Warn("--collector.qdisk.device-exclude is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-exclude") *collectorQdiscDeviceExclude = *oldCollectorQdiskDeviceExclude } else { return nil, fmt.Errorf("--collector.qdisk.device-exclude and --collector.qdisc.device-exclude are mutually exclusive") diff --git a/collector/rapl_linux.go b/collector/rapl_linux.go index 642de6c1..25d78f29 100644 --- a/collector/rapl_linux.go +++ b/collector/rapl_linux.go @@ -19,12 +19,11 @@ package collector import ( "errors" "fmt" + "log/slog" "os" "strconv" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -33,7 +32,7 @@ const raplCollectorSubsystem = "rapl" type raplCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger joulesMetricDesc *prometheus.Desc } @@ -47,7 +46,7 @@ var ( ) // NewRaplCollector returns a new Collector exposing RAPL metrics. -func NewRaplCollector(logger log.Logger) (Collector, error) { +func NewRaplCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { @@ -74,11 +73,11 @@ func (c *raplCollector) Update(ch chan<- prometheus.Metric) error { zones, err := sysfs.GetRaplZones(c.fs) if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "Platform doesn't have powercap files present", "err", err) + c.logger.Debug("Platform doesn't have powercap files present", "err", err) return ErrNoData } if errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "Can't access powercap files", "err", err) + c.logger.Debug("Can't access powercap files", "err", err) return ErrNoData } return fmt.Errorf("failed to retrieve rapl stats: %w", err) @@ -88,7 +87,7 @@ func (c *raplCollector) Update(ch chan<- prometheus.Metric) error { microJoules, err := rz.GetEnergyMicrojoules() if err != nil { if errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "Can't access energy_uj file", "zone", rz, "err", err) + c.logger.Debug("Can't access energy_uj file", "zone", rz, "err", err) return ErrNoData } return err diff --git a/collector/runit.go b/collector/runit.go index 3cae657c..8065d90c 100644 --- a/collector/runit.go +++ b/collector/runit.go @@ -18,10 +18,9 @@ package collector import ( "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus-community/go-runit/runit" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) var runitServiceDir = kingpin.Flag("collector.runit.servicedir", "Path to runit service directory.").Default("/etc/service").String() @@ -31,7 +30,7 @@ type runitCollector struct { stateDesired typedDesc stateNormal typedDesc stateTimestamp typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -39,14 +38,14 @@ func init() { } // NewRunitCollector returns a new Collector exposing runit statistics. -func NewRunitCollector(logger log.Logger) (Collector, error) { +func NewRunitCollector(logger *slog.Logger) (Collector, error) { var ( subsystem = "service" constLabels = prometheus.Labels{"supervisor": "runit"} labelNames = []string{"service"} ) - level.Warn(logger).Log("msg", "This collector is deprecated and will be removed in the next major version release.") + logger.Warn("This collector is deprecated and will be removed in the next major version release.") return &runitCollector{ state: typedDesc{prometheus.NewDesc( @@ -82,11 +81,11 @@ func (c *runitCollector) Update(ch chan<- prometheus.Metric) error { for _, service := range services { status, err := service.Status() if err != nil { - level.Debug(c.logger).Log("msg", "Couldn't get status", "service", service.Name, "err", err) + c.logger.Debug("Couldn't get status", "service", service.Name, "err", err) continue } - level.Debug(c.logger).Log("msg", "duration", "service", service.Name, "status", status.State, "pid", status.Pid, "duration_seconds", status.Duration) + c.logger.Debug("duration", "service", service.Name, "status", status.State, "pid", status.Pid, "duration_seconds", status.Duration) ch <- c.state.mustNewConstMetric(float64(status.State), service.Name) ch <- c.stateDesired.mustNewConstMetric(float64(status.Want), service.Name) ch <- c.stateTimestamp.mustNewConstMetric(float64(status.Timestamp.Unix()), service.Name) diff --git a/collector/schedstat_linux.go b/collector/schedstat_linux.go index e5016561..59b2bd8e 100644 --- a/collector/schedstat_linux.go +++ b/collector/schedstat_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -53,7 +52,7 @@ var ( ) // NewSchedstatCollector returns a new Collector exposing task scheduler statistics -func NewSchedstatCollector(logger log.Logger) (Collector, error) { +func NewSchedstatCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -64,7 +63,7 @@ func NewSchedstatCollector(logger log.Logger) (Collector, error) { type schedstatCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -75,7 +74,7 @@ func (c *schedstatCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.Schedstat() if err != nil { if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "schedstat file does not exist") + c.logger.Debug("schedstat file does not exist") return ErrNoData } return err diff --git a/collector/selinux_linux.go b/collector/selinux_linux.go index 79316362..9000fe0d 100644 --- a/collector/selinux_linux.go +++ b/collector/selinux_linux.go @@ -17,16 +17,16 @@ package collector import ( - "github.com/go-kit/log" "github.com/opencontainers/selinux/go-selinux" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) type selinuxCollector struct { configMode *prometheus.Desc currentMode *prometheus.Desc enabled *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +34,7 @@ func init() { } // NewSelinuxCollector returns a new Collector exposing SELinux statistics. -func NewSelinuxCollector(logger log.Logger) (Collector, error) { +func NewSelinuxCollector(logger *slog.Logger) (Collector, error) { const subsystem = "selinux" return &selinuxCollector{ diff --git a/collector/slabinfo_linux.go b/collector/slabinfo_linux.go index a3c3ebce..aa871548 100644 --- a/collector/slabinfo_linux.go +++ b/collector/slabinfo_linux.go @@ -18,33 +18,41 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" + "github.com/alecthomas/kingpin/v2" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) +var ( + slabNameInclude = kingpin.Flag("collector.slabinfo.slabs-include", "Regexp of slabs to include in slabinfo collector.").Default(".*").String() + slabNameExclude = kingpin.Flag("collector.slabinfo.slabs-exclude", "Regexp of slabs to exclude in slabinfo collector.").Default("").String() +) + type slabinfoCollector struct { - fs procfs.FS - logger log.Logger - subsystem string - labels []string + fs procfs.FS + logger *slog.Logger + subsystem string + labels []string + slabNameFilter deviceFilter } func init() { registerCollector("slabinfo", defaultDisabled, NewSlabinfoCollector) } -func NewSlabinfoCollector(logger log.Logger) (Collector, error) { +func NewSlabinfoCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) } return &slabinfoCollector{logger: logger, - fs: fs, - subsystem: "slabinfo", - labels: []string{"slab"}, + fs: fs, + subsystem: "slabinfo", + labels: []string{"slab"}, + slabNameFilter: newDeviceFilter(*slabNameExclude, *slabNameInclude), }, nil } @@ -55,6 +63,9 @@ func (c *slabinfoCollector) Update(ch chan<- prometheus.Metric) error { } for _, slab := range slabinfo.Slabs { + if c.slabNameFilter.ignored(slab.Name) { + continue + } ch <- c.activeObjects(slab.Name, slab.ObjActive) ch <- c.objects(slab.Name, slab.ObjNum) ch <- c.objectSizeBytes(slab.Name, slab.ObjSize) diff --git a/collector/sockstat_linux.go b/collector/sockstat_linux.go index 480c83b1..73a3bf68 100644 --- a/collector/sockstat_linux.go +++ b/collector/sockstat_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -35,7 +34,7 @@ const ( var pageSize = os.Getpagesize() type sockStatCollector struct { - logger log.Logger + logger *slog.Logger } func init() { @@ -43,7 +42,7 @@ func init() { } // NewSockStatCollector returns a new Collector exposing socket stats. -func NewSockStatCollector(logger log.Logger) (Collector, error) { +func NewSockStatCollector(logger *slog.Logger) (Collector, error) { return &sockStatCollector{logger}, nil } @@ -58,7 +57,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { switch { case err == nil: case errors.Is(err, os.ErrNotExist): - level.Debug(c.logger).Log("msg", "IPv4 sockstat statistics not found, skipping") + c.logger.Debug("IPv4 sockstat statistics not found, skipping") default: return fmt.Errorf("failed to get IPv4 sockstat data: %w", err) } @@ -67,7 +66,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { switch { case err == nil: case errors.Is(err, os.ErrNotExist): - level.Debug(c.logger).Log("msg", "IPv6 sockstat statistics not found, skipping") + c.logger.Debug("IPv6 sockstat statistics not found, skipping") default: return fmt.Errorf("failed to get IPv6 sockstat data: %w", err) } diff --git a/collector/softirqs_common.go b/collector/softirqs_common.go index 08ef780f..cd9ccb94 100644 --- a/collector/softirqs_common.go +++ b/collector/softirqs_common.go @@ -18,15 +18,15 @@ package collector import ( "fmt" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" + "log/slog" ) type softirqsCollector struct { fs procfs.FS desc typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +34,7 @@ func init() { } // NewSoftirqsCollector returns a new Collector exposing softirq stats. -func NewSoftirqsCollector(logger log.Logger) (Collector, error) { +func NewSoftirqsCollector(logger *slog.Logger) (Collector, error) { desc := typedDesc{prometheus.NewDesc( namespace+"_softirqs_functions_total", "Softirq counts per CPU.", diff --git a/collector/softnet_linux.go b/collector/softnet_linux.go index 42d47780..296e5875 100644 --- a/collector/softnet_linux.go +++ b/collector/softnet_linux.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "strconv" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -34,7 +34,7 @@ type softnetCollector struct { receivedRps *prometheus.Desc flowLimitCount *prometheus.Desc softnetBacklogLen *prometheus.Desc - logger log.Logger + logger *slog.Logger } const ( @@ -46,7 +46,7 @@ func init() { } // NewSoftnetCollector returns a new Collector exposing softnet metrics. -func NewSoftnetCollector(logger log.Logger) (Collector, error) { +func NewSoftnetCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/stat_linux.go b/collector/stat_linux.go index 9974ae7a..0b99cc32 100644 --- a/collector/stat_linux.go +++ b/collector/stat_linux.go @@ -18,9 +18,9 @@ package collector import ( "fmt" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -34,7 +34,7 @@ type statCollector struct { procsRunning *prometheus.Desc procsBlocked *prometheus.Desc softIRQ *prometheus.Desc - logger log.Logger + logger *slog.Logger } var statSoftirqFlag = kingpin.Flag("collector.stat.softirq", "Export softirq calls per vector").Default("false").Bool() @@ -44,7 +44,7 @@ func init() { } // NewStatCollector returns a new Collector exposing kernel/system statistics. -func NewStatCollector(logger log.Logger) (Collector, error) { +func NewStatCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/supervisord.go b/collector/supervisord.go index 9b517f0a..ae5e2297 100644 --- a/collector/supervisord.go +++ b/collector/supervisord.go @@ -19,14 +19,13 @@ package collector import ( "context" "fmt" + "log/slog" "net" "net/http" "net/url" "time" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/mattn/go-xmlrpc" "github.com/prometheus/client_golang/prometheus" ) @@ -41,7 +40,7 @@ type supervisordCollector struct { stateDesc *prometheus.Desc exitStatusDesc *prometheus.Desc startTimeDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -49,7 +48,7 @@ func init() { } // NewSupervisordCollector returns a new Collector exposing supervisord statistics. -func NewSupervisordCollector(logger log.Logger) (Collector, error) { +func NewSupervisordCollector(logger *slog.Logger) (Collector, error) { var ( subsystem = "supervisord" labelNames = []string{"name", "group"} @@ -69,7 +68,7 @@ func NewSupervisordCollector(logger log.Logger) (Collector, error) { xrpc = xmlrpc.NewClient(*supervisordURL) } - level.Warn(logger).Log("msg", "This collector is deprecated and will be removed in the next major version release.") + logger.Warn("This collector is deprecated and will be removed in the next major version release.") return &supervisordCollector{ upDesc: prometheus.NewDesc( @@ -174,7 +173,7 @@ func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error { } else { ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 0, labels...) } - level.Debug(c.logger).Log("msg", "process info", "group", info.Group, "name", info.Name, "state", info.StateName, "pid", info.PID) + c.logger.Debug("process info", "group", info.Group, "name", info.Name, "state", info.StateName, "pid", info.PID) } return nil diff --git a/collector/sysctl_linux.go b/collector/sysctl_linux.go index c14341db..ac7022fb 100644 --- a/collector/sysctl_linux.go +++ b/collector/sysctl_linux.go @@ -15,11 +15,11 @@ package collector import ( "fmt" + "log/slog" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -33,7 +33,7 @@ var ( type sysctlCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger sysctls []*sysctl } @@ -41,7 +41,7 @@ func init() { registerCollector("sysctl", defaultDisabled, NewSysctlCollector) } -func NewSysctlCollector(logger log.Logger) (Collector, error) { +func NewSysctlCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) diff --git a/collector/systemd_linux.go b/collector/systemd_linux.go index 33232511..ee2ded8d 100644 --- a/collector/systemd_linux.go +++ b/collector/systemd_linux.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "log/slog" "math" "regexp" "strconv" @@ -29,8 +30,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/coreos/go-systemd/v22/dbus" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -75,10 +74,10 @@ type systemdCollector struct { socketCurrentConnectionsDesc *prometheus.Desc socketRefusedConnectionsDesc *prometheus.Desc systemdVersionDesc *prometheus.Desc - // Use regexps for more flexability than device_filter.go allows + // Use regexps for more flexibility than device_filter.go allows systemdUnitIncludePattern *regexp.Regexp systemdUnitExcludePattern *regexp.Regexp - logger log.Logger + logger *slog.Logger } var unitStatesName = []string{"active", "activating", "deactivating", "inactive", "failed"} @@ -88,7 +87,7 @@ func init() { } // NewSystemdCollector returns a new Collector exposing systemd statistics. -func NewSystemdCollector(logger log.Logger) (Collector, error) { +func NewSystemdCollector(logger *slog.Logger) (Collector, error) { const subsystem = "systemd" unitDesc := prometheus.NewDesc( @@ -136,7 +135,7 @@ func NewSystemdCollector(logger log.Logger) (Collector, error) { if *oldSystemdUnitExclude != "" { if !systemdUnitExcludeSet { - level.Warn(logger).Log("msg", "--collector.systemd.unit-blacklist is DEPRECATED and will be removed in 2.0.0, use --collector.systemd.unit-exclude") + logger.Warn("--collector.systemd.unit-blacklist is DEPRECATED and will be removed in 2.0.0, use --collector.systemd.unit-exclude") *systemdUnitExclude = *oldSystemdUnitExclude } else { return nil, errors.New("--collector.systemd.unit-blacklist and --collector.systemd.unit-exclude are mutually exclusive") @@ -144,15 +143,15 @@ func NewSystemdCollector(logger log.Logger) (Collector, error) { } if *oldSystemdUnitInclude != "" { if !systemdUnitIncludeSet { - level.Warn(logger).Log("msg", "--collector.systemd.unit-whitelist is DEPRECATED and will be removed in 2.0.0, use --collector.systemd.unit-include") + logger.Warn("--collector.systemd.unit-whitelist is DEPRECATED and will be removed in 2.0.0, use --collector.systemd.unit-include") *systemdUnitInclude = *oldSystemdUnitInclude } else { return nil, errors.New("--collector.systemd.unit-whitelist and --collector.systemd.unit-include are mutually exclusive") } } - level.Info(logger).Log("msg", "Parsed flag --collector.systemd.unit-include", "flag", *systemdUnitInclude) + logger.Info("Parsed flag --collector.systemd.unit-include", "flag", *systemdUnitInclude) systemdUnitIncludePattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *systemdUnitInclude)) - level.Info(logger).Log("msg", "Parsed flag --collector.systemd.unit-exclude", "flag", *systemdUnitExclude) + logger.Info("Parsed flag --collector.systemd.unit-exclude", "flag", *systemdUnitExclude) systemdUnitExcludePattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *systemdUnitExclude)) return &systemdCollector{ @@ -186,7 +185,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { systemdVersion, systemdVersionFull := c.getSystemdVersion(conn) if systemdVersion < minSystemdVersionSystemState { - level.Debug(c.logger).Log("msg", "Detected systemd version is lower than minimum, some systemd state and timer metrics will not be available", "current", systemdVersion, "minimum", minSystemdVersionSystemState) + c.logger.Debug("Detected systemd version is lower than minimum, some systemd state and timer metrics will not be available", "current", systemdVersion, "minimum", minSystemdVersionSystemState) } ch <- prometheus.MustNewConstMetric( c.systemdVersionDesc, @@ -199,16 +198,16 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get units: %w", err) } - level.Debug(c.logger).Log("msg", "getAllUnits took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("getAllUnits took", "duration_seconds", time.Since(begin).Seconds()) begin = time.Now() summary := summarizeUnits(allUnits) c.collectSummaryMetrics(ch, summary) - level.Debug(c.logger).Log("msg", "collectSummaryMetrics took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectSummaryMetrics took", "duration_seconds", time.Since(begin).Seconds()) begin = time.Now() units := filterUnits(allUnits, c.systemdUnitIncludePattern, c.systemdUnitExcludePattern, c.logger) - level.Debug(c.logger).Log("msg", "filterUnits took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("filterUnits took", "duration_seconds", time.Since(begin).Seconds()) var wg sync.WaitGroup defer wg.Wait() @@ -218,7 +217,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitStatusMetrics(conn, ch, units) - level.Debug(c.logger).Log("msg", "collectUnitStatusMetrics took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectUnitStatusMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() if *enableStartTimeMetrics { @@ -227,7 +226,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitStartTimeMetrics(conn, ch, units) - level.Debug(c.logger).Log("msg", "collectUnitStartTimeMetrics took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectUnitStartTimeMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -237,7 +236,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitTasksMetrics(conn, ch, units) - level.Debug(c.logger).Log("msg", "collectUnitTasksMetrics took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectUnitTasksMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -247,7 +246,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectTimers(conn, ch, units) - level.Debug(c.logger).Log("msg", "collectTimers took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectTimers took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -256,13 +255,13 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectSockets(conn, ch, units) - level.Debug(c.logger).Log("msg", "collectSockets took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectSockets took", "duration_seconds", time.Since(begin).Seconds()) }() if systemdVersion >= minSystemdVersionSystemState { begin = time.Now() err = c.collectSystemState(conn, ch) - level.Debug(c.logger).Log("msg", "collectSystemState took", "duration_seconds", time.Since(begin).Seconds()) + c.logger.Debug("collectSystemState took", "duration_seconds", time.Since(begin).Seconds()) } return err @@ -274,14 +273,14 @@ func (c *systemdCollector) collectUnitStatusMetrics(conn *dbus.Conn, ch chan<- p if strings.HasSuffix(unit.Name, ".service") { serviceTypeProperty, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Service", "Type") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit type", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit type", "unit", unit.Name, "err", err) } else { serviceType = serviceTypeProperty.Value.Value().(string) } } else if strings.HasSuffix(unit.Name, ".mount") { serviceTypeProperty, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Mount", "Type") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit type", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit type", "unit", unit.Name, "err", err) } else { serviceType = serviceTypeProperty.Value.Value().(string) } @@ -299,7 +298,7 @@ func (c *systemdCollector) collectUnitStatusMetrics(conn *dbus.Conn, ch chan<- p // NRestarts wasn't added until systemd 235. restartsCount, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Service", "NRestarts") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit NRestarts", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit NRestarts", "unit", unit.Name, "err", err) } else { ch <- prometheus.MustNewConstMetric( c.nRestartsDesc, prometheus.CounterValue, @@ -317,7 +316,7 @@ func (c *systemdCollector) collectSockets(conn *dbus.Conn, ch chan<- prometheus. acceptedConnectionCount, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Socket", "NAccepted") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit NAccepted", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit NAccepted", "unit", unit.Name, "err", err) continue } ch <- prometheus.MustNewConstMetric( @@ -326,7 +325,7 @@ func (c *systemdCollector) collectSockets(conn *dbus.Conn, ch chan<- prometheus. currentConnectionCount, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Socket", "NConnections") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit NConnections", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit NConnections", "unit", unit.Name, "err", err) continue } ch <- prometheus.MustNewConstMetric( @@ -352,7 +351,7 @@ func (c *systemdCollector) collectUnitStartTimeMetrics(conn *dbus.Conn, ch chan< } else { timestampValue, err := conn.GetUnitPropertyContext(context.TODO(), unit.Name, "ActiveEnterTimestamp") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit StartTimeUsec", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit StartTimeUsec", "unit", unit.Name, "err", err) continue } startTimeUsec = timestampValue.Value.Value().(uint64) @@ -370,7 +369,7 @@ func (c *systemdCollector) collectUnitTasksMetrics(conn *dbus.Conn, ch chan<- pr if strings.HasSuffix(unit.Name, ".service") { tasksCurrentCount, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Service", "TasksCurrent") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit TasksCurrent", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit TasksCurrent", "unit", unit.Name, "err", err) } else { val = tasksCurrentCount.Value.Value().(uint64) // Don't set if tasksCurrent if dbus reports MaxUint64. @@ -382,7 +381,7 @@ func (c *systemdCollector) collectUnitTasksMetrics(conn *dbus.Conn, ch chan<- pr } tasksMaxCount, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Service", "TasksMax") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit TasksMax", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit TasksMax", "unit", unit.Name, "err", err) } else { val = tasksMaxCount.Value.Value().(uint64) // Don't set if tasksMax if dbus reports MaxUint64. @@ -404,7 +403,7 @@ func (c *systemdCollector) collectTimers(conn *dbus.Conn, ch chan<- prometheus.M lastTriggerValue, err := conn.GetUnitTypePropertyContext(context.TODO(), unit.Name, "Timer", "LastTriggerUSec") if err != nil { - level.Debug(c.logger).Log("msg", "couldn't get unit LastTriggerUSec", "unit", unit.Name, "err", err) + c.logger.Debug("couldn't get unit LastTriggerUSec", "unit", unit.Name, "err", err) continue } @@ -476,14 +475,14 @@ func summarizeUnits(units []unit) map[string]float64 { return summarized } -func filterUnits(units []unit, includePattern, excludePattern *regexp.Regexp, logger log.Logger) []unit { +func filterUnits(units []unit, includePattern, excludePattern *regexp.Regexp, logger *slog.Logger) []unit { filtered := make([]unit, 0, len(units)) for _, unit := range units { if includePattern.MatchString(unit.Name) && !excludePattern.MatchString(unit.Name) && unit.LoadState == "loaded" { - level.Debug(logger).Log("msg", "Adding unit", "unit", unit.Name) + logger.Debug("Adding unit", "unit", unit.Name) filtered = append(filtered, unit) } else { - level.Debug(logger).Log("msg", "Ignoring unit", "unit", unit.Name) + logger.Debug("Ignoring unit", "unit", unit.Name) } } @@ -493,15 +492,15 @@ func filterUnits(units []unit, includePattern, excludePattern *regexp.Regexp, lo func (c *systemdCollector) getSystemdVersion(conn *dbus.Conn) (float64, string) { version, err := conn.GetManagerProperty("Version") if err != nil { - level.Debug(c.logger).Log("msg", "Unable to get systemd version property, defaulting to 0") + c.logger.Debug("Unable to get systemd version property, defaulting to 0") return 0, "" } version = strings.TrimPrefix(strings.TrimSuffix(version, `"`), `"`) - level.Debug(c.logger).Log("msg", "Got systemd version", "version", version) + c.logger.Debug("Got systemd version", "version", version) parsedVersion := systemdVersionRE.FindString(version) v, err := strconv.ParseFloat(parsedVersion, 64) if err != nil { - level.Debug(c.logger).Log("msg", "Got invalid systemd version", "version", version) + c.logger.Debug("Got invalid systemd version", "version", version) return 0, "" } return v, version diff --git a/collector/systemd_linux_test.go b/collector/systemd_linux_test.go index d4e300d1..1c290377 100644 --- a/collector/systemd_linux_test.go +++ b/collector/systemd_linux_test.go @@ -17,11 +17,12 @@ package collector import ( + "io" + "log/slog" "regexp" "testing" "github.com/coreos/go-systemd/v22/dbus" - "github.com/go-kit/log" ) // Creates mock UnitLists @@ -94,7 +95,7 @@ func TestSystemdIgnoreFilter(t *testing.T) { fixtures := getUnitListFixtures() includePattern := regexp.MustCompile("^foo$") excludePattern := regexp.MustCompile("^bar$") - filtered := filterUnits(fixtures[0], includePattern, excludePattern, log.NewNopLogger()) + filtered := filterUnits(fixtures[0], includePattern, excludePattern, slog.New(slog.NewTextHandler(io.Discard, nil))) for _, unit := range filtered { if excludePattern.MatchString(unit.Name) || !includePattern.MatchString(unit.Name) { t.Error(unit.Name, "should not be in the filtered list") @@ -102,7 +103,7 @@ func TestSystemdIgnoreFilter(t *testing.T) { } } func TestSystemdIgnoreFilterDefaultKeepsAll(t *testing.T) { - logger := log.NewNopLogger() + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) c, err := NewSystemdCollector(logger) if err != nil { t.Fatal(err) diff --git a/collector/tapestats_linux.go b/collector/tapestats_linux.go index 264c2210..c74c11d0 100644 --- a/collector/tapestats_linux.go +++ b/collector/tapestats_linux.go @@ -18,12 +18,11 @@ package collector import ( "fmt" + "log/slog" "os" "regexp" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -45,7 +44,7 @@ type tapestatsCollector struct { writeTimeSeconds *prometheus.Desc residualTotal *prometheus.Desc fs sysfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -54,7 +53,7 @@ func init() { // NewTapestatsCollector returns a new Collector exposing tape device stats. // Docs from https://www.kernel.org/doc/html/latest/scsi/st.html#sysfs-and-statistics-for-tape-devices -func NewTapestatsCollector(logger log.Logger) (Collector, error) { +func NewTapestatsCollector(logger *slog.Logger) (Collector, error) { var tapeLabelNames = []string{"device"} fs, err := sysfs.NewFS(*sysPath) @@ -126,7 +125,7 @@ func (c *tapestatsCollector) Update(ch chan<- prometheus.Metric) error { tapes, err := c.fs.SCSITapeClass() if err != nil { if os.IsNotExist(err) { - level.Debug(c.logger).Log("msg", "scsi_tape stats not found, skipping") + c.logger.Debug("scsi_tape stats not found, skipping") return ErrNoData } return fmt.Errorf("error obtaining SCSITape class info: %s", err) @@ -134,7 +133,7 @@ func (c *tapestatsCollector) Update(ch chan<- prometheus.Metric) error { for _, tape := range tapes { if c.ignoredDevicesPattern.MatchString(tape.Name) { - level.Debug(c.logger).Log("msg", "Ignoring device", "device", tape.Name) + c.logger.Debug("Ignoring device", "device", tape.Name) continue } ch <- prometheus.MustNewConstMetric(c.ioNow, prometheus.GaugeValue, float64(tape.Counters.InFlight), tape.Name) diff --git a/collector/tcpstat_linux.go b/collector/tcpstat_linux.go index 99e33bc6..476a9b47 100644 --- a/collector/tcpstat_linux.go +++ b/collector/tcpstat_linux.go @@ -18,11 +18,11 @@ package collector import ( "fmt" + "log/slog" "os" "syscall" "unsafe" - "github.com/go-kit/log" "github.com/mdlayher/netlink" "github.com/prometheus/client_golang/prometheus" ) @@ -60,7 +60,7 @@ const ( type tcpStatCollector struct { desc typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -68,7 +68,7 @@ func init() { } // NewTCPStatCollector returns a new Collector exposing network stats. -func NewTCPStatCollector(logger log.Logger) (Collector, error) { +func NewTCPStatCollector(logger *slog.Logger) (Collector, error) { return &tcpStatCollector{ desc: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "tcp", "connection_states"), diff --git a/collector/textfile.go b/collector/textfile.go index 48133f7c..134083af 100644 --- a/collector/textfile.go +++ b/collector/textfile.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "log/slog" "os" "path/filepath" "sort" @@ -25,16 +26,14 @@ import ( "time" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" ) var ( - textFileDirectory = kingpin.Flag("collector.textfile.directory", "Directory to read text files with metrics from.").Default("").String() - mtimeDesc = prometheus.NewDesc( + textFileDirectories = kingpin.Flag("collector.textfile.directory", "Directory to read text files with metrics from, supports glob matching. (repeatable)").Default("").Strings() + mtimeDesc = prometheus.NewDesc( "node_textfile_mtime_seconds", "Unixtime mtime of textfiles successfully read.", []string{"file"}, @@ -43,10 +42,10 @@ var ( ) type textFileCollector struct { - path string + paths []string // Only set for testing to get predictable output. mtime *float64 - logger log.Logger + logger *slog.Logger } func init() { @@ -55,15 +54,15 @@ func init() { // NewTextFileCollector returns a new Collector exposing metrics read from files // in the given textfile directory. -func NewTextFileCollector(logger log.Logger) (Collector, error) { +func NewTextFileCollector(logger *slog.Logger) (Collector, error) { c := &textFileCollector{ - path: *textFileDirectory, + paths: *textFileDirectories, logger: logger, } return c, nil } -func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric, logger log.Logger) { +func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric, logger *slog.Logger) { var valType prometheus.ValueType var val float64 @@ -79,7 +78,7 @@ func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Me for _, metric := range metricFamily.Metric { if metric.TimestampMs != nil { - level.Warn(logger).Log("msg", "Ignoring unsupported custom timestamp on textfile collector metric", "metric", metric) + logger.Warn("Ignoring unsupported custom timestamp on textfile collector metric", "metric", metric) } labels := metric.GetLabel() @@ -193,12 +192,17 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { var errored bool var parsedFamilies []*dto.MetricFamily metricsNamesToFiles := map[string][]string{} + metricsNamesToHelpTexts := map[string][2]string{} - paths, err := filepath.Glob(c.path) - if err != nil || len(paths) == 0 { - // not glob or not accessible path either way assume single - // directory and let os.ReadDir handle it - paths = []string{c.path} + paths := []string{} + for _, glob := range c.paths { + ps, err := filepath.Glob(glob) + if err != nil || len(ps) == 0 { + // not glob or not accessible path either way assume single + // directory and let os.ReadDir handle it + ps = []string{glob} + } + paths = append(paths, ps...) } mtimes := make(map[string]time.Time) @@ -206,7 +210,7 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { files, err := os.ReadDir(path) if err != nil && path != "" { errored = true - level.Error(c.logger).Log("msg", "failed to read textfile collector directory", "path", path, "err", err) + c.logger.Error("failed to read textfile collector directory", "path", path, "err", err) } for _, f := range files { @@ -218,13 +222,30 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { mtime, families, err := c.processFile(path, f.Name(), ch) for _, mf := range families { + // Check for metrics with inconsistent help texts and take the first help text occurrence. + if helpTexts, seen := metricsNamesToHelpTexts[*mf.Name]; seen { + if mf.Help != nil && helpTexts[0] != *mf.Help || helpTexts[1] != "" { + metricsNamesToHelpTexts[*mf.Name] = [2]string{helpTexts[0], *mf.Help} + errored = true + c.logger.Error("inconsistent metric help text", + "metric", *mf.Name, + "original_help_text", helpTexts[0], + "new_help_text", *mf.Help, + // Only the first file path will be recorded in case of two or more inconsistent help texts. + "file", metricsNamesToFiles[*mf.Name][0]) + continue + } + } + if mf.Help != nil { + metricsNamesToHelpTexts[*mf.Name] = [2]string{*mf.Help} + } metricsNamesToFiles[*mf.Name] = append(metricsNamesToFiles[*mf.Name], metricsFilePath) parsedFamilies = append(parsedFamilies, mf) } if err != nil { errored = true - level.Error(c.logger).Log("msg", "failed to collect textfile data", "file", f.Name(), "err", err) + c.logger.Error("failed to collect textfile data", "file", f.Name(), "err", err) continue } diff --git a/collector/textfile_test.go b/collector/textfile_test.go index 95e5966f..ece70b3a 100644 --- a/collector/textfile_test.go +++ b/collector/textfile_test.go @@ -18,17 +18,18 @@ package collector import ( "fmt" + "io" + "log/slog" "net/http" "net/http/httptest" "os" "testing" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/promlog" - "github.com/prometheus/common/promlog/flag" + "github.com/prometheus/common/promslog" + "github.com/prometheus/common/promslog/flag" ) type collectorAdapter struct { @@ -51,83 +52,90 @@ func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) { func TestTextfileCollector(t *testing.T) { tests := []struct { - path string - out string + paths []string + out string }{ { - path: "fixtures/textfile/no_metric_files", - out: "fixtures/textfile/no_metric_files.out", + paths: []string{"fixtures/textfile/no_metric_files"}, + out: "fixtures/textfile/no_metric_files.out", }, { - path: "fixtures/textfile/two_metric_files", - out: "fixtures/textfile/two_metric_files.out", + paths: []string{"fixtures/textfile/two_metric_files"}, + out: "fixtures/textfile/two_metric_files.out", }, { - path: "fixtures/textfile/nonexistent_path", - out: "fixtures/textfile/nonexistent_path.out", + paths: []string{"fixtures/textfile/nonexistent_path"}, + out: "fixtures/textfile/nonexistent_path.out", }, { - path: "fixtures/textfile/client_side_timestamp", - out: "fixtures/textfile/client_side_timestamp.out", + paths: []string{"fixtures/textfile/client_side_timestamp"}, + out: "fixtures/textfile/client_side_timestamp.out", }, { - path: "fixtures/textfile/different_metric_types", - out: "fixtures/textfile/different_metric_types.out", + paths: []string{"fixtures/textfile/different_metric_types"}, + out: "fixtures/textfile/different_metric_types.out", }, { - path: "fixtures/textfile/inconsistent_metrics", - out: "fixtures/textfile/inconsistent_metrics.out", + paths: []string{"fixtures/textfile/inconsistent_metrics"}, + out: "fixtures/textfile/inconsistent_metrics.out", }, { - path: "fixtures/textfile/histogram", - out: "fixtures/textfile/histogram.out", + paths: []string{"fixtures/textfile/histogram"}, + out: "fixtures/textfile/histogram.out", }, { - path: "fixtures/textfile/histogram_extra_dimension", - out: "fixtures/textfile/histogram_extra_dimension.out", + paths: []string{"fixtures/textfile/histogram_extra_dimension"}, + out: "fixtures/textfile/histogram_extra_dimension.out", }, { - path: "fixtures/textfile/summary", - out: "fixtures/textfile/summary.out", + paths: []string{"fixtures/textfile/summary"}, + out: "fixtures/textfile/summary.out", }, { - path: "fixtures/textfile/summary_extra_dimension", - out: "fixtures/textfile/summary_extra_dimension.out", + paths: []string{"fixtures/textfile/summary_extra_dimension"}, + out: "fixtures/textfile/summary_extra_dimension.out", }, { - path: "fixtures/textfile/*_extra_dimension", - out: "fixtures/textfile/glob_extra_dimension.out", + paths: []string{ + "fixtures/textfile/histogram_extra_dimension", + "fixtures/textfile/summary_extra_dimension", + }, + out: "fixtures/textfile/glob_extra_dimension.out", }, { - path: "fixtures/textfile/metrics_merge_empty_help", - out: "fixtures/textfile/metrics_merge_empty_help.out", + paths: []string{"fixtures/textfile/*_extra_dimension"}, + out: "fixtures/textfile/glob_extra_dimension.out", }, { - path: "fixtures/textfile/metrics_merge_no_help", - out: "fixtures/textfile/metrics_merge_no_help.out", + paths: []string{"fixtures/textfile/metrics_merge_empty_help"}, + out: "fixtures/textfile/metrics_merge_empty_help.out", }, { - path: "fixtures/textfile/metrics_merge_same_help", - out: "fixtures/textfile/metrics_merge_same_help.out", + paths: []string{"fixtures/textfile/metrics_merge_no_help"}, + out: "fixtures/textfile/metrics_merge_no_help.out", }, { - path: "fixtures/textfile/metrics_merge_different_help", - out: "fixtures/textfile/metrics_merge_different_help.out", + paths: []string{"fixtures/textfile/metrics_merge_same_help"}, + out: "fixtures/textfile/metrics_merge_same_help.out", + }, + { + paths: []string{"fixtures/textfile/metrics_merge_different_help"}, + out: "fixtures/textfile/metrics_merge_different_help.out", }, } for i, test := range tests { mtime := 1.0 c := &textFileCollector{ - path: test.path, + paths: test.paths, mtime: &mtime, - logger: log.NewNopLogger(), + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), } // Suppress a log message about `nonexistent_path` not existing, this is // expected and clutters the test output. - promlogConfig := &promlog.Config{} - flag.AddFlags(kingpin.CommandLine, promlogConfig) + promslogConfig := &promslog.Config{} + flag.AddFlags(kingpin.CommandLine, promslogConfig) if _, err := kingpin.CommandLine.Parse([]string{"--log.level", "debug"}); err != nil { t.Fatal(err) } @@ -145,7 +153,7 @@ func TestTextfileCollector(t *testing.T) { } if string(want) != got { - t.Fatalf("%d.%q want:\n\n%s\n\ngot:\n\n%s", i, test.path, string(want), got) + t.Fatalf("%d.%q want:\n\n%s\n\ngot:\n\n%s", i, test.paths, string(want), got) } } } diff --git a/collector/thermal_darwin.go b/collector/thermal_darwin.go index 25673dcc..0a3b8981 100644 --- a/collector/thermal_darwin.go +++ b/collector/thermal_darwin.go @@ -47,9 +47,9 @@ import "C" import ( "errors" "fmt" + "log/slog" "unsafe" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -57,7 +57,7 @@ type thermCollector struct { cpuSchedulerLimit typedDesc cpuAvailableCPU typedDesc cpuSpeedLimit typedDesc - logger log.Logger + logger *slog.Logger } const thermal = "thermal" @@ -67,7 +67,7 @@ func init() { } // NewThermCollector returns a new Collector exposing current CPU power levels. -func NewThermCollector(logger log.Logger) (Collector, error) { +func NewThermCollector(logger *slog.Logger) (Collector, error) { return &thermCollector{ cpuSchedulerLimit: typedDesc{ desc: prometheus.NewDesc( diff --git a/collector/thermal_zone_linux.go b/collector/thermal_zone_linux.go index 6eff2732..95db2723 100644 --- a/collector/thermal_zone_linux.go +++ b/collector/thermal_zone_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -35,7 +34,7 @@ type thermalZoneCollector struct { coolingDeviceCurState *prometheus.Desc coolingDeviceMaxState *prometheus.Desc zoneTemp *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -43,7 +42,7 @@ func init() { } // NewThermalZoneCollector returns a new Collector exposing kernel/system statistics. -func NewThermalZoneCollector(logger log.Logger) (Collector, error) { +func NewThermalZoneCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -74,7 +73,7 @@ func (c *thermalZoneCollector) Update(ch chan<- prometheus.Metric) error { thermalZones, err := c.fs.ClassThermalZoneStats() if err != nil { if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrInvalid) { - level.Debug(c.logger).Log("msg", "Could not read thermal zone stats", "err", err) + c.logger.Debug("Could not read thermal zone stats", "err", err) return ErrNoData } return err diff --git a/collector/time.go b/collector/time.go index 31a6e74e..a4a81687 100644 --- a/collector/time.go +++ b/collector/time.go @@ -17,10 +17,9 @@ package collector import ( + "log/slog" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -29,7 +28,7 @@ type timeCollector struct { zone typedDesc clocksourcesAvailable typedDesc clocksourceCurrent typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -38,7 +37,7 @@ func init() { // NewTimeCollector returns a new Collector exposing the current system time in // seconds since epoch. -func NewTimeCollector(logger log.Logger) (Collector, error) { +func NewTimeCollector(logger *slog.Logger) (Collector, error) { const subsystem = "time" return &timeCollector{ now: typedDesc{prometheus.NewDesc( @@ -70,9 +69,9 @@ func (c *timeCollector) Update(ch chan<- prometheus.Metric) error { nowSec := float64(now.UnixNano()) / 1e9 zone, zoneOffset := now.Zone() - level.Debug(c.logger).Log("msg", "Return time", "now", nowSec) + c.logger.Debug("Return time", "now", nowSec) ch <- c.now.mustNewConstMetric(nowSec) - level.Debug(c.logger).Log("msg", "Zone offset", "offset", zoneOffset, "time_zone", zone) + c.logger.Debug("Zone offset", "offset", zoneOffset, "time_zone", zone) ch <- c.zone.mustNewConstMetric(float64(zoneOffset), zone) return c.update(ch) } diff --git a/collector/time_linux.go b/collector/time_linux.go index dd4afe75..212ba228 100644 --- a/collector/time_linux.go +++ b/collector/time_linux.go @@ -20,7 +20,6 @@ import ( "fmt" "strconv" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -35,7 +34,7 @@ func (c *timeCollector) update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get clocksources: %w", err) } - level.Debug(c.logger).Log("msg", "in Update", "clocksources", fmt.Sprintf("%v", clocksources)) + c.logger.Debug("in Update", "clocksources", fmt.Sprintf("%v", clocksources)) for i, clocksource := range clocksources { is := strconv.Itoa(i) diff --git a/collector/timex.go b/collector/timex.go index 69cbc1a1..eb21f7c3 100644 --- a/collector/timex.go +++ b/collector/timex.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -61,7 +60,7 @@ type timexCollector struct { stbcnt, tai, syncStatus typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -69,7 +68,7 @@ func init() { } // NewTimexCollector returns a new Collector exposing adjtime(3) stats. -func NewTimexCollector(logger log.Logger) (Collector, error) { +func NewTimexCollector(logger *slog.Logger) (Collector, error) { const subsystem = "timex" return &timexCollector{ @@ -170,7 +169,7 @@ func (c *timexCollector) Update(ch chan<- prometheus.Metric) error { status, err := unix.Adjtimex(timex) if err != nil { if errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "Not collecting timex metrics", "err", err) + c.logger.Debug("Not collecting timex metrics", "err", err) return ErrNoData } return fmt.Errorf("failed to retrieve adjtimex stats: %w", err) diff --git a/collector/udp_queues_linux.go b/collector/udp_queues_linux.go index 3fac8691..2923936e 100644 --- a/collector/udp_queues_linux.go +++ b/collector/udp_queues_linux.go @@ -19,10 +19,9 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -31,7 +30,7 @@ type ( udpQueuesCollector struct { fs procfs.FS desc *prometheus.Desc - logger log.Logger + logger *slog.Logger } ) @@ -40,7 +39,7 @@ func init() { } // NewUDPqueuesCollector returns a new Collector exposing network udp queued bytes. -func NewUDPqueuesCollector(logger log.Logger) (Collector, error) { +func NewUDPqueuesCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -64,7 +63,7 @@ func (c *udpQueuesCollector) Update(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, float64(s4.RxQueueLength), "rx", "v4") } else { if errors.Is(errIPv4, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "not collecting ipv4 based metrics") + c.logger.Debug("not collecting ipv4 based metrics") } else { return fmt.Errorf("couldn't get udp queued bytes: %w", errIPv4) } @@ -76,7 +75,7 @@ func (c *udpQueuesCollector) Update(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, float64(s6.RxQueueLength), "rx", "v6") } else { if errors.Is(errIPv6, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "not collecting ipv6 based metrics") + c.logger.Debug("not collecting ipv6 based metrics") } else { return fmt.Errorf("couldn't get udp6 queued bytes: %w", errIPv6) } diff --git a/collector/uname.go b/collector/uname.go index 76e66b7e..6b4f06e5 100644 --- a/collector/uname.go +++ b/collector/uname.go @@ -11,15 +11,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || freebsd || openbsd || netbsd || linux) && !nouname -// +build darwin freebsd openbsd netbsd linux +//go:build (darwin || freebsd || openbsd || netbsd || linux || aix) && !nouname +// +build darwin freebsd openbsd netbsd linux aix // +build !nouname package collector import ( - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) var unameDesc = prometheus.NewDesc( @@ -37,7 +37,7 @@ var unameDesc = prometheus.NewDesc( ) type unameCollector struct { - logger log.Logger + logger *slog.Logger } type uname struct { SysName string @@ -53,7 +53,7 @@ func init() { } // NewUnameCollector returns new unameCollector. -func newUnameCollector(logger log.Logger) (Collector, error) { +func newUnameCollector(logger *slog.Logger) (Collector, error) { return &unameCollector{logger}, nil } diff --git a/collector/uname_bsd.go b/collector/uname_bsd.go index 69bf38e9..fa565a1c 100644 --- a/collector/uname_bsd.go +++ b/collector/uname_bsd.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || freebsd || openbsd || netbsd) && !nouname -// +build darwin freebsd openbsd netbsd +//go:build (darwin || freebsd || openbsd || netbsd || aix) && !nouname +// +build darwin freebsd openbsd netbsd aix // +build !nouname package collector diff --git a/collector/utils/utils.go b/collector/utils/utils.go new file mode 100644 index 00000000..aa5b7540 --- /dev/null +++ b/collector/utils/utils.go @@ -0,0 +1,27 @@ +// Copyright 2024 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 utils + +func SafeDereference[T any](s ...*T) []T { + var resolved []T + for _, v := range s { + if v != nil { + resolved = append(resolved, *v) + } else { + var zeroValue T + resolved = append(resolved, zeroValue) + } + } + return resolved +} diff --git a/collector/vmstat_linux.go b/collector/vmstat_linux.go index cde2df5d..e31be0f7 100644 --- a/collector/vmstat_linux.go +++ b/collector/vmstat_linux.go @@ -19,13 +19,13 @@ package collector import ( "bufio" "fmt" + "log/slog" "os" "regexp" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -39,7 +39,7 @@ var ( type vmStatCollector struct { fieldPattern *regexp.Regexp - logger log.Logger + logger *slog.Logger } func init() { @@ -47,7 +47,7 @@ func init() { } // NewvmStatCollector returns a new Collector exposing vmstat stats. -func NewvmStatCollector(logger log.Logger) (Collector, error) { +func NewvmStatCollector(logger *slog.Logger) (Collector, error) { pattern := regexp.MustCompile(*vmStatFields) return &vmStatCollector{ fieldPattern: pattern, diff --git a/collector/watchdog.go b/collector/watchdog.go index 01351be9..77fb792e 100644 --- a/collector/watchdog.go +++ b/collector/watchdog.go @@ -19,17 +19,16 @@ package collector import ( "errors" "fmt" + "log/slog" "os" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) type watchdogCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -37,7 +36,7 @@ func init() { } // NewWatchdogCollector returns a new Collector exposing watchdog stats. -func NewWatchdogCollector(logger log.Logger) (Collector, error) { +func NewWatchdogCollector(logger *slog.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -103,7 +102,7 @@ func (c *watchdogCollector) Update(ch chan<- prometheus.Metric) error { watchdogClass, err := c.fs.WatchdogClass() if err != nil { if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrInvalid) { - level.Debug(c.logger).Log("msg", "Could not read watchdog stats", "err", err) + c.logger.Debug("Could not read watchdog stats", "err", err) return ErrNoData } return err diff --git a/collector/watchdog_test.go b/collector/watchdog_test.go index e59382b4..baf08995 100644 --- a/collector/watchdog_test.go +++ b/collector/watchdog_test.go @@ -18,11 +18,11 @@ package collector import ( "fmt" - "os" + "io" + "log/slog" "strings" "testing" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" ) @@ -68,7 +68,7 @@ func TestWatchdogStats(t *testing.T) { ` *sysPath = "fixtures/sys" - logger := log.NewLogfmtLogger(os.Stderr) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) c, err := NewWatchdogCollector(logger) if err != nil { t.Fatal(err) diff --git a/collector/wifi_linux.go b/collector/wifi_linux.go index aff8eb2d..c84cdd71 100644 --- a/collector/wifi_linux.go +++ b/collector/wifi_linux.go @@ -20,12 +20,11 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "os" "path/filepath" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/mdlayher/wifi" "github.com/prometheus/client_golang/prometheus" ) @@ -45,7 +44,7 @@ type wifiCollector struct { stationTransmitFailedTotal *prometheus.Desc stationBeaconLossTotal *prometheus.Desc - logger log.Logger + logger *slog.Logger } var ( @@ -67,7 +66,7 @@ type wifiStater interface { } // NewWifiCollector returns a new Collector exposing Wifi statistics. -func NewWifiCollector(logger log.Logger) (Collector, error) { +func NewWifiCollector(logger *slog.Logger) (Collector, error) { const ( subsystem = "wifi" ) @@ -169,11 +168,11 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { // Cannot access wifi metrics, report no error. if errors.Is(err, os.ErrNotExist) { - level.Debug(c.logger).Log("msg", "wifi collector metrics are not available for this system") + c.logger.Debug("wifi collector metrics are not available for this system") return ErrNoData } if errors.Is(err, os.ErrPermission) { - level.Debug(c.logger).Log("msg", "wifi collector got permission denied when accessing metrics") + c.logger.Debug("wifi collector got permission denied when accessing metrics") return ErrNoData } @@ -192,7 +191,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { continue } - level.Debug(c.logger).Log("msg", "probing wifi device with type", "wifi", ifi.Name, "type", ifi.Type) + c.logger.Debug("probing wifi device with type", "wifi", ifi.Name, "type", ifi.Type) ch <- prometheus.MustNewConstMetric( c.interfaceFrequencyHertz, @@ -210,7 +209,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { case err == nil: c.updateBSSStats(ch, ifi.Name, bss) case errors.Is(err, os.ErrNotExist): - level.Debug(c.logger).Log("msg", "BSS information not found for wifi device", "name", ifi.Name) + c.logger.Debug("BSS information not found for wifi device", "name", ifi.Name) default: return fmt.Errorf("failed to retrieve BSS for device %s: %v", ifi.Name, err) @@ -223,7 +222,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { c.updateStationStats(ch, ifi.Name, station) } case errors.Is(err, os.ErrNotExist): - level.Debug(c.logger).Log("msg", "station information not found for wifi device", "name", ifi.Name) + c.logger.Debug("station information not found for wifi device", "name", ifi.Name) default: return fmt.Errorf("failed to retrieve station info for device %q: %v", ifi.Name, err) diff --git a/collector/xfrm.go b/collector/xfrm.go index cbdcc97f..d96ee8aa 100644 --- a/collector/xfrm.go +++ b/collector/xfrm.go @@ -18,15 +18,15 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) type xfrmCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +34,7 @@ func init() { } // NewXfrmCollector returns a new Collector exposing XFRM stats. -func NewXfrmCollector(logger log.Logger) (Collector, error) { +func NewXfrmCollector(logger *slog.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) diff --git a/collector/xfrm_test.go b/collector/xfrm_test.go index 2e1ac028..e009fc40 100644 --- a/collector/xfrm_test.go +++ b/collector/xfrm_test.go @@ -18,11 +18,11 @@ package collector import ( "fmt" - "os" + "io" + "log/slog" "strings" "testing" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" ) @@ -127,7 +127,7 @@ func TestXfrmStats(t *testing.T) { ` *procPath = "fixtures/proc" - logger := log.NewLogfmtLogger(os.Stderr) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) c, err := NewXfrmCollector(logger) if err != nil { t.Fatal(err) diff --git a/collector/xfs_linux.go b/collector/xfs_linux.go index bb25acab..3eac1d0e 100644 --- a/collector/xfs_linux.go +++ b/collector/xfs_linux.go @@ -18,8 +18,8 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/xfs" ) @@ -27,7 +27,7 @@ import ( // An xfsCollector is a Collector which gathers metrics from XFS filesystems. type xfsCollector struct { fs xfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -35,7 +35,7 @@ func init() { } // NewXFSCollector returns a new Collector exposing XFS statistics. -func NewXFSCollector(logger log.Logger) (Collector, error) { +func NewXFSCollector(logger *slog.Logger) (Collector, error) { fs, err := xfs.NewFS(*procPath, *sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) diff --git a/collector/zfs.go b/collector/zfs.go deleted file mode 100644 index df1a97ae..00000000 --- a/collector/zfs.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2016 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 linux && !nozfs -// +build linux,!nozfs - -package collector - -import ( - "errors" - "strings" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -var errZFSNotAvailable = errors.New("ZFS / ZFS statistics are not available") - -type zfsSysctl string - -func init() { - registerCollector("zfs", defaultEnabled, NewZFSCollector) -} - -type zfsCollector struct { - linuxProcpathBase string - linuxZpoolIoPath string - linuxZpoolObjsetPath string - linuxZpoolStatePath string - linuxPathMap map[string]string - logger log.Logger -} - -// NewZFSCollector returns a new Collector exposing ZFS statistics. -func NewZFSCollector(logger log.Logger) (Collector, error) { - return &zfsCollector{ - linuxProcpathBase: "spl/kstat/zfs", - linuxZpoolIoPath: "/*/io", - linuxZpoolObjsetPath: "/*/objset-*", - linuxZpoolStatePath: "/*/state", - linuxPathMap: map[string]string{ - "zfs_abd": "abdstats", - "zfs_arc": "arcstats", - "zfs_dbuf": "dbufstats", - "zfs_dmu_tx": "dmu_tx", - "zfs_dnode": "dnodestats", - "zfs_fm": "fm", - "zfs_vdev_cache": "vdev_cache_stats", // vdev_cache is deprecated - "zfs_vdev_mirror": "vdev_mirror_stats", - "zfs_xuio": "xuio_stats", // no known consumers of the XUIO interface on Linux exist - "zfs_zfetch": "zfetchstats", - "zfs_zil": "zil", - }, - logger: logger, - }, nil -} - -func (c *zfsCollector) Update(ch chan<- prometheus.Metric) error { - - if _, err := c.openProcFile(c.linuxProcpathBase); err != nil { - if err == errZFSNotAvailable { - level.Debug(c.logger).Log("err", err) - return ErrNoData - } - } - - for subsystem := range c.linuxPathMap { - if err := c.updateZfsStats(subsystem, ch); err != nil { - if err == errZFSNotAvailable { - level.Debug(c.logger).Log("err", err) - // ZFS /proc files are added as new features to ZFS arrive, it is ok to continue - continue - } - return err - } - } - - // Pool stats - return c.updatePoolStats(ch) -} - -func (s zfsSysctl) metricName() string { - parts := strings.Split(string(s), ".") - return strings.Replace(parts[len(parts)-1], "-", "_", -1) -} - -func (c *zfsCollector) constSysctlMetric(subsystem string, sysctl zfsSysctl, value uint64) prometheus.Metric { - metricName := sysctl.metricName() - - return prometheus.MustNewConstMetric( - prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, metricName), - string(sysctl), - nil, - nil, - ), - prometheus.UntypedValue, - float64(value), - ) -} - -func (c *zfsCollector) constPoolMetric(poolName string, sysctl zfsSysctl, value uint64) prometheus.Metric { - metricName := sysctl.metricName() - - return prometheus.MustNewConstMetric( - prometheus.NewDesc( - prometheus.BuildFQName(namespace, "zfs_zpool", metricName), - string(sysctl), - []string{"zpool"}, - nil, - ), - prometheus.UntypedValue, - float64(value), - poolName, - ) -} - -func (c *zfsCollector) constPoolObjsetMetric(poolName string, datasetName string, sysctl zfsSysctl, value uint64) prometheus.Metric { - metricName := sysctl.metricName() - - return prometheus.MustNewConstMetric( - prometheus.NewDesc( - prometheus.BuildFQName(namespace, "zfs_zpool_dataset", metricName), - string(sysctl), - []string{"zpool", "dataset"}, - nil, - ), - prometheus.UntypedValue, - float64(value), - poolName, - datasetName, - ) -} - -func (c *zfsCollector) constPoolStateMetric(poolName string, stateName string, isActive uint64) prometheus.Metric { - return prometheus.MustNewConstMetric( - prometheus.NewDesc( - prometheus.BuildFQName(namespace, "zfs_zpool", "state"), - "kstat.zfs.misc.state", - []string{"zpool", "state"}, - nil, - ), - prometheus.GaugeValue, - float64(isActive), - poolName, - stateName, - ) -} diff --git a/collector/zfs_common.go b/collector/zfs_common.go new file mode 100644 index 00000000..7934ec5a --- /dev/null +++ b/collector/zfs_common.go @@ -0,0 +1,22 @@ +// Copyright 2016 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 !nozfs && (freebsd || linux || solaris) +// +build !nozfs +// +build freebsd linux solaris + +package collector + +func init() { + registerCollector("zfs", defaultEnabled, NewZFSCollector) +} diff --git a/collector/zfs_freebsd.go b/collector/zfs_freebsd.go index bc4aa1c3..d888c3a7 100644 --- a/collector/zfs_freebsd.go +++ b/collector/zfs_freebsd.go @@ -17,25 +17,20 @@ package collector import ( - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" + "log/slog" ) type zfsCollector struct { sysctls []bsdSysctl - logger log.Logger + logger *slog.Logger } const ( zfsCollectorSubsystem = "zfs" ) -func init() { - registerCollector("zfs", defaultEnabled, NewZfsCollector) -} - -func NewZfsCollector(logger log.Logger) (Collector, error) { +func NewZFSCollector(logger *slog.Logger) (Collector, error) { return &zfsCollector{ sysctls: []bsdSysctl{ { @@ -273,7 +268,7 @@ func (c *zfsCollector) Update(ch chan<- prometheus.Metric) error { v, err := m.Value() if err != nil { // debug logging - level.Debug(c.logger).Log("name", m.name, "couldn't get sysctl:", err) + c.logger.Debug(m.name, "mib", m.mib, "couldn't get sysctl:", err) continue } diff --git a/collector/zfs_linux.go b/collector/zfs_linux.go index ec195b3d..e0d9c3f6 100644 --- a/collector/zfs_linux.go +++ b/collector/zfs_linux.go @@ -18,14 +18,15 @@ package collector import ( "bufio" + "errors" "fmt" "io" + "log/slog" "os" "path/filepath" "strconv" "strings" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -42,7 +43,67 @@ const ( // kstatDataString = "7" ) -var zfsPoolStatesName = []string{"online", "degraded", "faulted", "offline", "removed", "unavail", "suspended"} +var ( + errZFSNotAvailable = errors.New("ZFS / ZFS statistics are not available") + + zfsPoolStatesName = [...]string{"online", "degraded", "faulted", "offline", "removed", "unavail", "suspended"} +) + +type zfsCollector struct { + linuxProcpathBase string + linuxZpoolIoPath string + linuxZpoolObjsetPath string + linuxZpoolStatePath string + linuxPathMap map[string]string + logger *slog.Logger +} + +// NewZFSCollector returns a new Collector exposing ZFS statistics. +func NewZFSCollector(logger *slog.Logger) (Collector, error) { + return &zfsCollector{ + linuxProcpathBase: "spl/kstat/zfs", + linuxZpoolIoPath: "/*/io", + linuxZpoolObjsetPath: "/*/objset-*", + linuxZpoolStatePath: "/*/state", + linuxPathMap: map[string]string{ + "zfs_abd": "abdstats", + "zfs_arc": "arcstats", + "zfs_dbuf": "dbufstats", + "zfs_dmu_tx": "dmu_tx", + "zfs_dnode": "dnodestats", + "zfs_fm": "fm", + "zfs_vdev_cache": "vdev_cache_stats", // vdev_cache is deprecated + "zfs_vdev_mirror": "vdev_mirror_stats", + "zfs_xuio": "xuio_stats", // no known consumers of the XUIO interface on Linux exist + "zfs_zfetch": "zfetchstats", + "zfs_zil": "zil", + }, + logger: logger, + }, nil +} + +func (c *zfsCollector) Update(ch chan<- prometheus.Metric) error { + if _, err := c.openProcFile(c.linuxProcpathBase); err != nil { + if err == errZFSNotAvailable { + c.logger.Debug(err.Error()) + return ErrNoData + } + } + + for subsystem := range c.linuxPathMap { + if err := c.updateZfsStats(subsystem, ch); err != nil { + if err == errZFSNotAvailable { + c.logger.Debug(err.Error()) + // ZFS /proc files are added as new features to ZFS arrive, it is ok to continue + continue + } + return err + } + } + + // Pool stats + return c.updatePoolStats(ch) +} func (c *zfsCollector) openProcFile(path string) (*os.File, error) { file, err := os.Open(procFilePath(path)) @@ -50,7 +111,7 @@ func (c *zfsCollector) openProcFile(path string) (*os.File, error) { // file not found error can occur if: // 1. zfs module is not loaded // 2. zfs version does not have the feature with metrics -- ok to ignore - level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", procFilePath(path)) + c.logger.Debug("Cannot open file for reading", "path", procFilePath(path)) return nil, errZFSNotAvailable } return file, nil @@ -63,8 +124,15 @@ func (c *zfsCollector) updateZfsStats(subsystem string, ch chan<- prometheus.Met } defer file.Close() - return c.parseProcfsFile(file, c.linuxPathMap[subsystem], func(s zfsSysctl, v uint64) { - ch <- c.constSysctlMetric(subsystem, s, v) + return c.parseProcfsFile(file, c.linuxPathMap[subsystem], func(s zfsSysctl, v interface{}) { + var valueAsFloat64 float64 + switch value := v.(type) { + case int64: + valueAsFloat64 = float64(value) + case uint64: + valueAsFloat64 = float64(value) + } + ch <- c.constSysctlMetric(subsystem, s, valueAsFloat64) }) } @@ -78,7 +146,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { file, err := os.Open(zpoolPath) if err != nil { // this file should exist, but there is a race where an exporting pool can remove the files -- ok to ignore - level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", zpoolPath) + c.logger.Debug("Cannot open file for reading", "path", zpoolPath) return errZFSNotAvailable } @@ -100,7 +168,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { file, err := os.Open(zpoolPath) if err != nil { // This file should exist, but there is a race where an exporting pool can remove the files. Ok to ignore. - level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", zpoolPath) + c.logger.Debug("Cannot open file for reading", "path", zpoolPath) return errZFSNotAvailable } @@ -119,7 +187,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { } if zpoolStatePaths == nil { - level.Debug(c.logger).Log("msg", "No pool state files found") + c.logger.Debug("No pool state files found") return nil } @@ -127,7 +195,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { file, err := os.Open(zpoolPath) if err != nil { // This file should exist, but there is a race where an exporting pool can remove the files. Ok to ignore. - level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", zpoolPath) + c.logger.Debug("Cannot open file for reading", "path", zpoolPath) return errZFSNotAvailable } @@ -144,7 +212,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { return nil } -func (c *zfsCollector) parseProcfsFile(reader io.Reader, fmtExt string, handler func(zfsSysctl, uint64)) error { +func (c *zfsCollector) parseProcfsFile(reader io.Reader, fmtExt string, handler func(zfsSysctl, interface{})) error { scanner := bufio.NewScanner(reader) parseLine := false @@ -163,11 +231,18 @@ func (c *zfsCollector) parseProcfsFile(reader io.Reader, fmtExt string, handler // kstat data type (column 2) should be KSTAT_DATA_UINT64, otherwise ignore // TODO: when other KSTAT_DATA_* types arrive, much of this will need to be restructured - if parts[1] == kstatDataUint64 || parts[1] == kstatDataInt64 { - key := fmt.Sprintf("kstat.zfs.misc.%s.%s", fmtExt, parts[0]) + key := fmt.Sprintf("kstat.zfs.misc.%s.%s", fmtExt, parts[0]) + switch parts[1] { + case kstatDataUint64: value, err := strconv.ParseUint(parts[2], 10, 64) if err != nil { - return fmt.Errorf("could not parse expected integer value for %q", key) + return fmt.Errorf("could not parse expected unsigned integer value for %q: %w", key, err) + } + handler(zfsSysctl(key), value) + case kstatDataInt64: + value, err := strconv.ParseInt(parts[2], 10, 64) + if err != nil { + return fmt.Errorf("could not parse expected signed integer value for %q: %w", key, err) } handler(zfsSysctl(key), value) } @@ -291,3 +366,73 @@ func (c *zfsCollector) parsePoolStateFile(reader io.Reader, zpoolPath string, ha return nil } + +func (c *zfsCollector) constSysctlMetric(subsystem string, sysctl zfsSysctl, value float64) prometheus.Metric { + metricName := sysctl.metricName() + + return prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, metricName), + string(sysctl), + nil, + nil, + ), + prometheus.UntypedValue, + value, + ) +} + +func (c *zfsCollector) constPoolMetric(poolName string, sysctl zfsSysctl, value uint64) prometheus.Metric { + metricName := sysctl.metricName() + + return prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, "zfs_zpool", metricName), + string(sysctl), + []string{"zpool"}, + nil, + ), + prometheus.UntypedValue, + float64(value), + poolName, + ) +} + +func (c *zfsCollector) constPoolObjsetMetric(poolName string, datasetName string, sysctl zfsSysctl, value uint64) prometheus.Metric { + metricName := sysctl.metricName() + + return prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, "zfs_zpool_dataset", metricName), + string(sysctl), + []string{"zpool", "dataset"}, + nil, + ), + prometheus.UntypedValue, + float64(value), + poolName, + datasetName, + ) +} + +func (c *zfsCollector) constPoolStateMetric(poolName string, stateName string, isActive uint64) prometheus.Metric { + return prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, "zfs_zpool", "state"), + "kstat.zfs.misc.state", + []string{"zpool", "state"}, + nil, + ), + prometheus.GaugeValue, + float64(isActive), + poolName, + stateName, + ) +} + +type zfsSysctl string + +func (s zfsSysctl) metricName() string { + parts := strings.Split(string(s), ".") + return strings.Replace(parts[len(parts)-1], "-", "_", -1) +} diff --git a/collector/zfs_linux_test.go b/collector/zfs_linux_test.go index 69e4ed8f..c96d91b5 100644 --- a/collector/zfs_linux_test.go +++ b/collector/zfs_linux_test.go @@ -35,24 +35,25 @@ func TestArcstatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(arcstatsFile, "arcstats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(arcstatsFile, "arcstats", func(s zfsSysctl, v interface{}) { - if s != zfsSysctl("kstat.zfs.misc.arcstats.hits") { + if s == zfsSysctl("kstat.zfs.misc.arcstats.hits") { + if v.(uint64) != 8772612 { + t.Fatalf("Incorrect value parsed from procfs data") + } + } else if s == zfsSysctl("kstat.zfs.misc.arcstats.memory_available_bytes") { + if v.(int64) != -922337203685477580 { + t.Fatalf("Incorrect value parsed from procfs data") + } + } else { return } handlerCalled = true - - if v != uint64(8772612) { - t.Fatalf("Incorrect value parsed from procfs data") - } - }) - if err != nil { t.Fatal(err) } - if !handlerCalled { t.Fatal("Arcstats parsing handler was not called for some expected sysctls") } @@ -71,7 +72,7 @@ func TestZfetchstatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(zfetchstatsFile, "zfetchstats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(zfetchstatsFile, "zfetchstats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.zfetchstats.hits") { return @@ -79,7 +80,7 @@ func TestZfetchstatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(7067992) { + if v.(uint64) != 7067992 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -107,7 +108,7 @@ func TestZilParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(zilFile, "zil", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(zilFile, "zil", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.zil.zil_commit_count") { return @@ -115,7 +116,7 @@ func TestZilParsing(t *testing.T) { handlerCalled = true - if v != uint64(10) { + if v.(uint64) != 10 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -143,7 +144,7 @@ func TestVdevCacheStatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(vdevCacheStatsFile, "vdev_cache_stats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(vdevCacheStatsFile, "vdev_cache_stats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.vdev_cache_stats.delegations") { return @@ -151,7 +152,7 @@ func TestVdevCacheStatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(40) { + if v.(uint64) != 40 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -179,7 +180,7 @@ func TestXuioStatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(xuioStatsFile, "xuio_stats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(xuioStatsFile, "xuio_stats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.xuio_stats.onloan_read_buf") { return @@ -187,7 +188,7 @@ func TestXuioStatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(32) { + if v.(uint64) != 32 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -215,7 +216,7 @@ func TestFmParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(fmFile, "fm", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(fmFile, "fm", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.fm.erpt-dropped") { return @@ -223,7 +224,7 @@ func TestFmParsing(t *testing.T) { handlerCalled = true - if v != uint64(18) { + if v.(uint64) != 18 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -251,7 +252,7 @@ func TestDmuTxParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(dmuTxFile, "dmu_tx", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(dmuTxFile, "dmu_tx", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.dmu_tx.dmu_tx_assigned") { return @@ -259,7 +260,7 @@ func TestDmuTxParsing(t *testing.T) { handlerCalled = true - if v != uint64(3532844) { + if v.(uint64) != 3532844 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -299,7 +300,7 @@ func TestZpoolParsing(t *testing.T) { handlerCalled = true - if v != uint64(1884160) && v != uint64(2826240) { + if v != 1884160 && v != 2826240 { t.Fatalf("Incorrect value parsed from procfs data %v", v) } @@ -339,7 +340,7 @@ func TestZpoolObjsetParsing(t *testing.T) { handlerCalled = true - if v != uint64(0) && v != uint64(4) && v != uint64(10) { + if v != 0 && v != 4 && v != 10 { t.Fatalf("Incorrect value parsed from procfs data %v", v) } @@ -367,7 +368,7 @@ func TestAbdstatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(abdstatsFile, "abdstats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(abdstatsFile, "abdstats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.abdstats.linear_data_size") { return @@ -375,7 +376,7 @@ func TestAbdstatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(223232) { + if v.(uint64) != 223232 { t.Fatalf("Incorrect value parsed from procfs abdstats data") } @@ -403,7 +404,7 @@ func TestDbufstatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(dbufstatsFile, "dbufstats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(dbufstatsFile, "dbufstats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.dbufstats.hash_hits") { return @@ -411,7 +412,7 @@ func TestDbufstatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(108807) { + if v.(uint64) != 108807 { t.Fatalf("Incorrect value parsed from procfs dbufstats data") } @@ -439,7 +440,7 @@ func TestDnodestatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(dnodestatsFile, "dnodestats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(dnodestatsFile, "dnodestats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.dnodestats.dnode_hold_alloc_hits") { return @@ -447,7 +448,7 @@ func TestDnodestatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(37617) { + if v.(uint64) != 37617 { t.Fatalf("Incorrect value parsed from procfs dnodestats data") } @@ -475,7 +476,7 @@ func TestVdevMirrorstatsParsing(t *testing.T) { } handlerCalled := false - err = c.parseProcfsFile(vdevMirrorStatsFile, "vdev_mirror_stats", func(s zfsSysctl, v uint64) { + err = c.parseProcfsFile(vdevMirrorStatsFile, "vdev_mirror_stats", func(s zfsSysctl, v interface{}) { if s != zfsSysctl("kstat.zfs.misc.vdev_mirror_stats.preferred_not_found") { return @@ -483,7 +484,7 @@ func TestVdevMirrorstatsParsing(t *testing.T) { handlerCalled = true - if v != uint64(94) { + if v.(uint64) != 94 { t.Fatalf("Incorrect value parsed from procfs vdev_mirror_stats data") } @@ -520,26 +521,26 @@ func TestPoolStateParsing(t *testing.T) { handlerCalled = true if poolName == "pool1" { - if isActive != uint64(1) && stateName == "online" { + if isActive != 1 && stateName == "online" { t.Fatalf("Incorrect parsed value for online state") } - if isActive != uint64(0) && stateName != "online" { + if isActive != 0 && stateName != "online" { t.Fatalf("Incorrect parsed value for online state") } } if poolName == "poolz1" { - if isActive != uint64(1) && stateName == "degraded" { + if isActive != 1 && stateName == "degraded" { t.Fatalf("Incorrect parsed value for degraded state") } - if isActive != uint64(0) && stateName != "degraded" { + if isActive != 0 && stateName != "degraded" { t.Fatalf("Incorrect parsed value for degraded state") } } if poolName == "pool2" { - if isActive != uint64(1) && stateName == "suspended" { + if isActive != 1 && stateName == "suspended" { t.Fatalf("Incorrect parsed value for suspended state") } - if isActive != uint64(0) && stateName != "suspended" { + if isActive != 0 && stateName != "suspended" { t.Fatalf("Incorrect parsed value for suspended state") } } diff --git a/collector/zfs_solaris.go b/collector/zfs_solaris.go index 52f2500f..1f10ee01 100644 --- a/collector/zfs_solaris.go +++ b/collector/zfs_solaris.go @@ -17,9 +17,9 @@ package collector import ( + "log/slog" "strings" - "github.com/go-kit/log" "github.com/illumos/go-kstat" "github.com/prometheus/client_golang/prometheus" ) @@ -54,18 +54,14 @@ type zfsCollector struct { arcstatsSize *prometheus.Desc zfetchstatsHits *prometheus.Desc zfetchstatsMisses *prometheus.Desc - logger log.Logger + logger *slog.Logger } const ( zfsCollectorSubsystem = "zfs" ) -func init() { - registerCollector("zfs", defaultEnabled, NewZfsCollector) -} - -func NewZfsCollector(logger log.Logger) (Collector, error) { +func NewZFSCollector(logger *slog.Logger) (Collector, error) { return &zfsCollector{ abdstatsLinearCount: prometheus.NewDesc( prometheus.BuildFQName(namespace, zfsCollectorSubsystem, "abdstats_linear_count_total"), diff --git a/collector/zoneinfo_linux.go b/collector/zoneinfo_linux.go index 8f7e35d9..0b40fd3c 100644 --- a/collector/zoneinfo_linux.go +++ b/collector/zoneinfo_linux.go @@ -15,10 +15,9 @@ package collector import ( "fmt" + "log/slog" "reflect" - "github.com/go-kit/log" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -28,7 +27,7 @@ const zoneinfoSubsystem = "zoneinfo" type zoneinfoCollector struct { gaugeMetricDescs map[string]*prometheus.Desc counterMetricDescs map[string]*prometheus.Desc - logger log.Logger + logger *slog.Logger fs procfs.FS } @@ -37,7 +36,7 @@ func init() { } // NewZoneinfoCollector returns a new Collector exposing zone stats. -func NewZoneinfoCollector(logger log.Logger) (Collector, error) { +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) diff --git a/docs/node-mixin/config.libsonnet b/docs/node-mixin/config.libsonnet index bf6c83ed..df2af23f 100644 --- a/docs/node-mixin/config.libsonnet +++ b/docs/node-mixin/config.libsonnet @@ -73,7 +73,7 @@ fsSpaceAvailableWarningThreshold: 5, fsSpaceAvailableCriticalThreshold: 3, - // Memory utilzation (%) level on which to trigger the + // Memory utilization (%) level on which to trigger the // 'NodeMemoryHighUtilization' alert. memoryHighUtilizationThreshold: 90, diff --git a/docs/node-mixin/dashboards/node.libsonnet b/docs/node-mixin/dashboards/node.libsonnet index 898c912d..41e29033 100644 --- a/docs/node-mixin/dashboards/node.libsonnet +++ b/docs/node-mixin/dashboards/node.libsonnet @@ -1,7 +1,8 @@ { local nodemixin = import '../lib/prom-mixin.libsonnet', grafanaDashboards+:: { - 'nodes.json': nodemixin.new(config=$._config, platform='Linux').dashboard, - 'nodes-darwin.json': nodemixin.new(config=$._config, platform='Darwin').dashboard, + 'nodes.json': nodemixin.new(config=$._config, platform='Linux', uid=std.md5('nodes.json')).dashboard, + 'nodes-darwin.json': nodemixin.new(config=$._config, platform='Darwin', uid=std.md5('nodes-darwin.json')).dashboard, + 'nodes-aix.json': nodemixin.new(config=$._config, platform='AIX', uid=std.md5('nodes-aix.json')).dashboard, }, } diff --git a/docs/node-mixin/dashboards/use.libsonnet b/docs/node-mixin/dashboards/use.libsonnet index 65e96dd8..fce411ab 100644 --- a/docs/node-mixin/dashboards/use.libsonnet +++ b/docs/node-mixin/dashboards/use.libsonnet @@ -150,7 +150,8 @@ local diskSpaceUtilisation = tags=($._config.dashboardTags), timezone='utc', refresh='30s', - graphTooltip='shared_crosshair' + graphTooltip='shared_crosshair', + uid=std.md5('node-rsrc-use.json') ) .addTemplate(datasourceTemplate) .addTemplate($._clusterTemplate) @@ -215,7 +216,8 @@ local diskSpaceUtilisation = tags=($._config.dashboardTags), timezone='utc', refresh='30s', - graphTooltip='shared_crosshair' + graphTooltip='shared_crosshair', + uid=std.md5('node-cluster-rsrc-use.json') ) .addTemplate(datasourceTemplate) .addTemplate($._clusterTemplate) @@ -326,7 +328,8 @@ local diskSpaceUtilisation = tags=($._config.dashboardTags), timezone='utc', refresh='30s', - graphTooltip='shared_crosshair' + graphTooltip='shared_crosshair', + uid=std.md5('node-multicluster-rsrc-use.json') ) .addTemplate(datasourceTemplate) .addRow( diff --git a/docs/node-mixin/lib/prom-mixin.libsonnet b/docs/node-mixin/lib/prom-mixin.libsonnet index 5d2e7bd0..bd01cfd4 100644 --- a/docs/node-mixin/lib/prom-mixin.libsonnet +++ b/docs/node-mixin/lib/prom-mixin.libsonnet @@ -10,7 +10,7 @@ local table = grafana70.panel.table; { - new(config=null, platform=null):: { + new(config=null, platform=null, uid=null):: { local prometheusDatasourceTemplate = { current: { @@ -147,7 +147,19 @@ local table = grafana70.panel.table; ||| % config, legendFormat='App Memory' )) .addTarget(prometheus.target('node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='Wired Memory')) - .addTarget(prometheus.target('node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='Compressed')), + .addTarget(prometheus.target('node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='Compressed')) + else if platform == 'AIX' then + memoryGraphPanelPrototype { stack: false } + .addTarget(prometheus.target('node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='Physical Memory')) + .addTarget(prometheus.target( + ||| + ( + node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - + node_memory_available_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + ) + ||| % config, legendFormat='Memory Used' + )), + // NOTE: avg() is used to circumvent a label change caused by a node_exporter rollout. local memoryGaugePanelPrototype = @@ -194,8 +206,21 @@ local table = grafana70.panel.table; * 100 ||| % config + )) + else if platform == 'AIX' then + memoryGaugePanelPrototype + .addTarget(prometheus.target( + ||| + 100 - + ( + avg(node_memory_available_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) / + avg(node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + * 100 + ) + ||| % config )), + local diskIO = graphPanel.new( 'Disk I/O', @@ -501,6 +526,7 @@ local table = grafana70.panel.table; tags=(config.dashboardTags), timezone='utc', refresh='30s', + uid=std.md5(uid), graphTooltip='shared_crosshair' ) .addTemplates(templates) @@ -512,6 +538,19 @@ local table = grafana70.panel.table; tags=(config.dashboardTags), timezone='utc', refresh='30s', + uid=std.md5(uid), + graphTooltip='shared_crosshair' + ) + .addTemplates(templates) + .addRows(rows) + else if platform == 'AIX' then + dashboard.new( + '%sAIX' % config.dashboardNamePrefix, + time_from='now-1h', + tags=(config.dashboardTags), + timezone='utc', + refresh='30s', + uid=std.md5(uid), graphTooltip='shared_crosshair' ) .addTemplates(templates) diff --git a/examples/systemd/node_exporter.service b/examples/systemd/node_exporter.service index 82553e91..1ef24442 100644 --- a/examples/systemd/node_exporter.service +++ b/examples/systemd/node_exporter.service @@ -4,7 +4,9 @@ Requires=node_exporter.socket [Service] User=node_exporter -EnvironmentFile=/etc/sysconfig/node_exporter +# Fallback when environment file does not exist +Environment=OPTIONS= +EnvironmentFile=-/etc/sysconfig/node_exporter ExecStart=/usr/sbin/node_exporter --web.systemd-socket $OPTIONS [Install] diff --git a/go.mod b/go.mod index ea6b91a6..25dfa21a 100644 --- a/go.mod +++ b/go.mod @@ -1,61 +1,61 @@ module github.com/prometheus/node_exporter -go 1.20 +go 1.22.0 require ( github.com/alecthomas/kingpin/v2 v2.4.0 - github.com/beevik/ntp v1.3.1 + github.com/beevik/ntp v1.4.3 github.com/coreos/go-systemd/v22 v22.5.0 - github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1 + github.com/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a github.com/ema/qdisc v1.0.0 - github.com/go-kit/log v0.2.1 github.com/godbus/dbus/v5 v5.1.0 github.com/hashicorp/go-envparse v0.1.0 github.com/hodgesds/perf-utils v0.7.0 github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973 github.com/josharian/native v1.1.0 - github.com/jsimonetti/rtnetlink v1.4.1 + github.com/jsimonetti/rtnetlink/v2 v2.0.2 github.com/lufia/iostat v1.2.1 github.com/mattn/go-xmlrpc v0.0.3 - github.com/mdlayher/ethtool v0.1.0 + github.com/mdlayher/ethtool v0.2.0 github.com/mdlayher/netlink v1.7.2 - github.com/mdlayher/wifi v0.1.0 + github.com/mdlayher/wifi v0.3.0 github.com/opencontainers/selinux v1.11.0 + 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.19.0 - github.com/prometheus/client_model v0.6.0 - github.com/prometheus/common v0.48.0 - github.com/prometheus/exporter-toolkit v0.11.0 - github.com/prometheus/procfs v0.13.0 - github.com/safchain/ethtool v0.3.0 - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 - golang.org/x/sys v0.18.0 + github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_model v0.6.1 + github.com/prometheus/common v0.60.0 + github.com/prometheus/exporter-toolkit v0.13.0 + github.com/prometheus/procfs v0.15.1 + github.com/safchain/ethtool v0.4.1 + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 + golang.org/x/sys v0.25.0 howett.net/plist v1.0.1 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dennwc/ioctl v1.0.0 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect + github.com/mdlayher/vsock v1.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/siebenmann/go-kstat v0.0.0-20210513183136-173c9b0a9973 // indirect 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.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 15f9dadb..4d9d0ed1 100644 --- a/go.sum +++ b/go.sum @@ -2,36 +2,28 @@ github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjH github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/beevik/ntp v1.3.1 h1:Y/srlT8L1yQr58kyPWFPZIxRL8ttx2SRIpVYJqZIlAM= -github.com/beevik/ntp v1.3.1/go.mod h1:fT6PylBq86Tsq23ZMEe47b7QQrZfYBFPnpzt0a9kJxw= +github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= +github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= +github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1 h1:ue4Es4Xzz255hWQ7NAWzZxuXG+YOV7URzzusLLSe0zU= -github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1/go.mod h1:MYsOV9Dgsec3FFSOjywi0QK5r6TeBbdWxdrMGtiYXHA= +github.com/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a h1:KfFsGLJFVdCXlySUkV2FmxNtmiztpJb6tV+XYBmmv8E= +github.com/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a/go.mod h1:MYsOV9Dgsec3FFSOjywi0QK5r6TeBbdWxdrMGtiYXHA= github.com/dennwc/ioctl v1.0.0 h1:DsWAAjIxRqNcLn9x6mwfuf2pet3iB7aK90K4tF16rLg= github.com/dennwc/ioctl v1.0.0/go.mod h1:ellh2YB5ldny99SBU/VX7Nq0xiZbHphf1DrtHxxjMk0= github.com/ema/qdisc v1.0.0 h1:EHLG08FVRbWLg8uRICa3xzC9Zm0m7HyMHfXobWFnXYg= github.com/ema/qdisc v1.0.0/go.mod h1:FhIc0fLYi7f+lK5maMsesDqwYojIOh3VfRs8EVd5YJQ= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= @@ -45,92 +37,98 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v1.4.1 h1:JfD4jthWBqZMEffc5RjgmlzpYttAVw1sdnmiNaPO3hE= -github.com/jsimonetti/rtnetlink v1.4.1/go.mod h1:xJjT7t59UIZ62GLZbv6PLLo8VFrostJMPBAheR6OM8w= +github.com/jsimonetti/rtnetlink/v2 v2.0.2 h1:ZKlbCujrIpp4/u3V2Ka0oxlf4BCkt6ojkvpy3nZoCBY= +github.com/jsimonetti/rtnetlink/v2 v2.0.2/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lufia/iostat v1.2.1 h1:tnCdZBIglgxD47RyD55kfWQcJMGzO+1QBziSQfesf2k= github.com/lufia/iostat v1.2.1/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= github.com/mattn/go-xmlrpc v0.0.3 h1:Y6WEMLEsqs3RviBrAa1/7qmbGB7DVD3brZIbqMbQdGY= github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA= -github.com/mdlayher/ethtool v0.1.0 h1:XAWHsmKhyPOo42qq/yTPb0eFBGUKKTR1rE0dVrWVQ0Y= -github.com/mdlayher/ethtool v0.1.0/go.mod h1:fBMLn2UhfRGtcH5ZFjr+6GUiHEjZsItFD7fSn7jbZVQ= +github.com/mdlayher/ethtool v0.2.0 h1:akcA4WZVWozzirPASeMq8qgLkxpF3ykftVXwnrMKrhY= +github.com/mdlayher/ethtool v0.2.0/go.mod h1:W0pIBrNPK1TslIN4Z9wt1EVbay66Kbvek2z2f29VBfw= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= 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/wifi v0.1.0 h1:y8wYRUXwok5CtUZOXT3egghYesX0O79E3ALl+SIDm9Q= -github.com/mdlayher/wifi v0.1.0/go.mod h1:+gBYnZAMcUKHSFzMJXwlz7tLsEHgwDJ9DJCefhJM+gI= +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.0 h1:ZfS81w/7xTWBJfhM77K0k6m3sJckwoNOoZUwOW34omo= +github.com/mdlayher/wifi v0.3.0/go.mod h1:/bdkqKYl+lD4recmQM6bTHxMrEUW70reibTyr93CAd0= +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= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus-community/go-runit v0.1.0 h1:uTWEj/Fn2RoLdfg/etSqwzgYNOYPrARx1BHUN052tGA= github.com/prometheus-community/go-runit v0.1.0/go.mod h1:AvJ9Jo3gAFu2lbM4+qfjdpq30FfiLDJZKbQ015u08IQ= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= -github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= -github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= -github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/exporter-toolkit v0.13.0 h1:lmA0Q+8IaXgmFRKw09RldZmZdnvu9wwcDLIXGmTPw1c= +github.com/prometheus/exporter-toolkit v0.13.0/go.mod h1:2uop99EZl80KdXhv/MxVI2181fMcwlsumFOqBecGkG0= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= -github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/safchain/ethtool v0.4.1 h1:S6mEleTADqgynileXoiapt/nKnatyR6bmIHoF+h2ADo= +github.com/safchain/ethtool v0.4.1/go.mod h1:XLLnZmy4OCRTkksP/UiMjij96YmIsBfmBQcs7H6tA48= github.com/siebenmann/go-kstat v0.0.0-20210513183136-173c9b0a9973 h1:GfSdC6wKfTGcgCS7BtzF5694Amne1pGCSTY252WhlEY= github.com/siebenmann/go-kstat v0.0.0-20210513183136-173c9b0a9973/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= 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.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= diff --git a/node_exporter.go b/node_exporter.go index cc24f7df..88441e1e 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -15,22 +15,22 @@ package main import ( "fmt" - stdlog "log" + "log/slog" "net/http" _ "net/http/pprof" "os" "os/user" "runtime" + "slices" "sort" - "github.com/prometheus/common/promlog" - "github.com/prometheus/common/promlog/flag" + "github.com/prometheus/common/promslog" + "github.com/prometheus/common/promslog/flag" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" promcollectors "github.com/prometheus/client_golang/prometheus/collectors" + versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/version" "github.com/prometheus/exporter-toolkit/web" @@ -43,15 +43,17 @@ import ( // newHandler. type handler struct { unfilteredHandler http.Handler + // enabledCollectors list is used for logging and filtering + enabledCollectors []string // exporterMetricsRegistry is a separate registry for the metrics about // the exporter itself. exporterMetricsRegistry *prometheus.Registry includeExporterMetrics bool maxRequests int - logger log.Logger + logger *slog.Logger } -func newHandler(includeExporterMetrics bool, maxRequests int, logger log.Logger) *handler { +func newHandler(includeExporterMetrics bool, maxRequests int, logger *slog.Logger) *handler { h := &handler{ exporterMetricsRegistry: prometheus.NewRegistry(), includeExporterMetrics: includeExporterMetrics, @@ -74,18 +76,41 @@ func newHandler(includeExporterMetrics bool, maxRequests int, logger log.Logger) // ServeHTTP implements http.Handler. func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - filters := r.URL.Query()["collect[]"] - level.Debug(h.logger).Log("msg", "collect query:", "filters", filters) + collects := r.URL.Query()["collect[]"] + h.logger.Debug("collect query:", "collects", collects) - if len(filters) == 0 { + excludes := r.URL.Query()["exclude[]"] + h.logger.Debug("exclude query:", "excludes", excludes) + + if len(collects) == 0 && len(excludes) == 0 { // No filters, use the prepared unfiltered handler. h.unfilteredHandler.ServeHTTP(w, r) return } + + if len(collects) > 0 && len(excludes) > 0 { + h.logger.Debug("rejecting combined collect and exclude queries") + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("Combined collect and exclude queries are not allowed.")) + return + } + + filters := &collects + if len(excludes) > 0 { + // In exclude mode, filtered collectors = enabled - excludeed. + f := []string{} + for _, c := range h.enabledCollectors { + if (slices.Index(excludes, c)) == -1 { + f = append(f, c) + } + } + filters = &f + } + // To serve filtered metrics, we create a filtering handler on the fly. - filteredHandler, err := h.innerHandler(filters...) + filteredHandler, err := h.innerHandler(*filters...) if err != nil { - level.Warn(h.logger).Log("msg", "Couldn't create filtered metrics handler:", "err", err) + 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))) return @@ -107,19 +132,18 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) { // Only log the creation of an unfiltered handler, which should happen // only once upon startup. if len(filters) == 0 { - level.Info(h.logger).Log("msg", "Enabled collectors") - collectors := []string{} + h.logger.Info("Enabled collectors") for n := range nc.Collectors { - collectors = append(collectors, n) + h.enabledCollectors = append(h.enabledCollectors, n) } - sort.Strings(collectors) - for _, c := range collectors { - level.Info(h.logger).Log("collector", c) + sort.Strings(h.enabledCollectors) + for _, c := range h.enabledCollectors { + h.logger.Info(c) } } r := prometheus.NewRegistry() - r.MustRegister(version.NewCollector("node_exporter")) + r.MustRegister(versioncollector.NewCollector("node_exporter")) if err := r.Register(nc); err != nil { return nil, fmt.Errorf("couldn't register node collector: %s", err) } @@ -129,7 +153,7 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) { handler = promhttp.HandlerFor( prometheus.Gatherers{h.exporterMetricsRegistry, r}, promhttp.HandlerOpts{ - ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(h.logger)), "", 0), + ErrorLog: slog.NewLogLogger(h.logger.Handler(), slog.LevelError), ErrorHandling: promhttp.ContinueOnError, MaxRequestsInFlight: h.maxRequests, Registry: h.exporterMetricsRegistry, @@ -144,7 +168,7 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) { handler = promhttp.HandlerFor( r, promhttp.HandlerOpts{ - ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(h.logger)), "", 0), + ErrorLog: slog.NewLogLogger(h.logger.Handler(), slog.LevelError), ErrorHandling: promhttp.ContinueOnError, MaxRequestsInFlight: h.maxRequests, }, @@ -178,24 +202,24 @@ func main() { toolkitFlags = kingpinflag.AddFlags(kingpin.CommandLine, ":9100") ) - promlogConfig := &promlog.Config{} - flag.AddFlags(kingpin.CommandLine, promlogConfig) + promslogConfig := &promslog.Config{} + flag.AddFlags(kingpin.CommandLine, promslogConfig) kingpin.Version(version.Print("node_exporter")) kingpin.CommandLine.UsageWriter(os.Stdout) kingpin.HelpFlag.Short('h') kingpin.Parse() - logger := promlog.New(promlogConfig) + logger := promslog.New(promslogConfig) if *disableDefaultCollectors { collector.DisableDefaultCollectors() } - level.Info(logger).Log("msg", "Starting node_exporter", "version", version.Info()) - level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) + logger.Info("Starting node_exporter", "version", version.Info()) + logger.Info("Build context", "build_context", version.BuildContext()) if user, err := user.Current(); err == nil && user.Uid == "0" { - level.Warn(logger).Log("msg", "Node Exporter is running as root user. This exporter is designed to run as unprivileged user, root is not required.") + logger.Warn("Node Exporter is running as root user. This exporter is designed to run as unprivileged user, root is not required.") } runtime.GOMAXPROCS(*maxProcs) - level.Debug(logger).Log("msg", "Go MAXPROCS", "procs", runtime.GOMAXPROCS(0)) + logger.Debug("Go MAXPROCS", "procs", runtime.GOMAXPROCS(0)) http.Handle(*metricsPath, newHandler(!*disableExporterMetrics, *maxRequests, logger)) if *metricsPath != "/" { @@ -212,7 +236,7 @@ func main() { } landingPage, err := web.NewLandingPage(landingConfig) if err != nil { - level.Error(logger).Log("err", err) + logger.Error(err.Error()) os.Exit(1) } http.Handle("/", landingPage) @@ -220,7 +244,7 @@ func main() { server := &http.Server{} if err := web.ListenAndServe(server, toolkitFlags, logger); err != nil { - level.Error(logger).Log("err", err) + logger.Error(err.Error()) os.Exit(1) } } diff --git a/ttar b/ttar index b0171a12..e6cade76 100755 --- a/ttar +++ b/ttar @@ -212,16 +212,16 @@ function extract { local eof_without_newline if [ "$size" -gt 0 ]; then if [[ "$line" =~ [^\\]EOF ]]; then - # An EOF not preceeded by a backslash indicates that the line + # An EOF not preceded by a backslash indicates that the line # does not end with a newline eof_without_newline=1 else eof_without_newline=0 fi # Replace NULLBYTE with null byte if at beginning of line - # Replace NULLBYTE with null byte unless preceeded by backslash + # Replace NULLBYTE with null byte unless preceded by backslash # Remove one backslash in front of NULLBYTE (if any) - # Remove EOF unless preceeded by backslash + # Remove EOF unless preceded by backslash # Remove one backslash in front of EOF if [ $USE_PYTHON -eq 1 ]; then echo -n "$line" | python -c "$PYTHON_EXTRACT_FILTER" >> "$path"