diff --git a/.circleci/config.yml b/.circleci/config.yml index 8cf200b8..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.22 + - 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: @@ -70,7 +70,7 @@ jobs: machine: image: ubuntu-2204:current environment: - DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.22-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/bsd.yml b/.github/workflows/bsd.yml new file mode 100644 index 00000000..09aa1b5c --- /dev/null +++ b/.github/workflows/bsd.yml @@ -0,0 +1,313 @@ +name: bsd + +on: + push: + branches: + - master + pull_request: + branches: + - master + +permissions: + contents: read + +env: + GNU_TAR_VERSION: "1.35" + GO_VERSION_DRAGONFLY: "1.23.3" + GO_VERSION_FREEBSD: "123" + GO_VERSION_NETBSD: "1.23.3" + GO_VERSION_OPENBSD: "1.23.1" + GO_VERSION_SOLARIS: "1.23.3" + +# To spin up one of the VMs below, see the "Debug Shell" section here: https://github.com/vmactions +jobs: + test_freebsd: + name: Run end-to-end tests on FreeBSD + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: test-e2e + uses: vmactions/freebsd-vm@v1 + with: + copyback: false + envs: 'GO_VERSION_FREEBSD GNU_TAR_VERSION' + usesh: true + prepare: | + pkg update -f + pkg install -y \ + bash \ + git \ + gmake \ + gnugrep \ + go${GO_VERSION_FREEBSD} \ + gsed \ + gtar \ + python \ + wget + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + ln -s $(which go${GO_VERSION_FREEBSD}) $(pwd)/bin/go + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + export PATH=$(pwd)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + gmake test-e2e + echo "::endgroup::" + + test_openbsd: + name: Run end-to-end tests on OpenBSD + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: test-e2e + uses: vmactions/openbsd-vm@v1 + with: + copyback: false + envs: 'GO_VERSION_OPENBSD GNU_TAR_VERSION' + usesh: true + prepare: | + pkg_add -u + pkg_add \ + bash \ + ggrep \ + git \ + gmake \ + go-${GO_VERSION_OPENBSD} \ + gsed \ + gtar-${GNU_TAR_VERSION}p0-static \ + python \ + wget + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + export PATH=$(pwd)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + make test-e2e + echo "::endgroup::" + + test_netbsd: + name: Run end-to-end tests on NetBSD + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: test-e2e + uses: vmactions/netbsd-vm@v1 + with: + copyback: false + envs: 'GO_VERSION_NETBSD GNU_TAR_VERSION' + usesh: true + prepare: | + /usr/sbin/pkg_add -u + /usr/sbin/pkg_add \ + git \ + gmake \ + grep \ + gsed \ + gtar-base-${GNU_TAR_VERSION}\ + python312 \ + wget + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + GOGZ="go${GO_VERSION_NETBSD}.netbsd-amd64.tar.gz" + wget https://go.dev/dl/${GOGZ} + gtar xzf ${GOGZ} + ln -s $(pwd)/go/bin/go $(pwd)/bin/go + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + export PATH=$(pwd)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + make test-e2e + echo "::endgroup::" + + test_dragonfly: + name: Run end-to-end tests on DragonFly + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: test-e2e + uses: vmactions/dragonflybsd-vm@v1 + with: + copyback: false + envs: 'GO_VERSION_DRAGONFLY' + usesh: true + prepare: | + pkg update && pkg upgrade -y + pkg install -y \ + bash \ + git \ + gmake \ + gnugrep \ + gsed \ + gtar \ + python3 \ + wget + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + GOGZ="go${GO_VERSION_DRAGONFLY}.dragonfly-amd64.tar.gz" + wget https://go.dev/dl/${GOGZ} + gtar xzf ${GOGZ} + ln -s $(pwd)/go/bin/go $(pwd)/bin/go + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + ln -s $(which python3) $(pwd)/bin/python + export PATH=$(pwd)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + gmake test-e2e + echo "::endgroup::" + + test_solaris: + name: Run end-to-end tests on Solaris + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: test-e2e + uses: vmactions/solaris-vm@v1 + with: + copyback: false + envs: 'GO_VERSION_SOLARIS' + usesh: true + prepare: | + pkg update + pkg install \ + bash \ + curl \ + gcc \ + git \ + gnu-grep \ + gnu-make \ + gnu-sed \ + gnu-tar + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + GOGZ="go${GO_VERSION_SOLARIS}.solaris-amd64.tar.gz" + wget https://go.dev/dl/${GOGZ} + gtar xzf ${GOGZ} + ln -s $(pwd)/go/bin/go $(pwd)/bin/go + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + export PATH=$(pwd)/bin:$PATH + + echo ">> building promu as it is not shipped for Solaris" + git clone https://github.com/prometheus/promu.git + cd promu + go build . + cd - + + mkdir -p $(go env GOPATH)/bin + ln -s $(pwd)/promu/promu $(go env GOPATH)/bin/promu + export PATH=$(go env GOPATH)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + make test-e2e + echo "::endgroup::" + + test_macos: + name: Run end-to-end tests on macOS + runs-on: macos-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Install dependencies + run: | + brew install \ + bash \ + curl \ + git \ + grep \ + make \ + gnu-sed \ + gnu-tar \ + go \ + python3 + - name: test-e2e + run: | + echo "::group::Setup prerequisites" + set -eu + mkdir bin + ln -s $(which ggrep) $(pwd)/bin/grep + ln -s $(which gmake) $(pwd)/bin/make + ln -s $(which gsed) $(pwd)/bin/sed + ln -s $(which gtar) $(pwd)/bin/tar + export PATH=$(pwd)/bin:$PATH + echo "::endgroup::" + + echo "::group::Print environment information" + uname -a + echo "GOOS: $(go env GOOS)" + echo "GOARCH: $(go env GOARCH)" + echo "::endgroup::" + + echo "::group::Run End-to-End Tests" + git config --global --add safe.directory $(pwd) + make test-e2e + echo "::endgroup::" diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index 8ddbc34a..dcca16ff 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -18,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@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set docker hub repo name run: echo "DOCKER_REPO_NAME=$(make docker-repo-name)" >> $GITHUB_ENV - name: Push README to Dockerhub @@ -40,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@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - 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 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index bb65d7f6..def9007a 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,16 +24,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 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@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 + uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0 with: args: --verbose - version: v1.59.1 + version: v1.63.4 diff --git a/.gitignore b/.gitignore index fcfd9727..c1318db3 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ dependencies-stamp /.release /.tarballs +tools/tools + # Intellij /.idea diff --git a/.golangci.yml b/.golangci.yml index 472b3a5e..8731aefd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,17 +1,9 @@ linters: enable: - depguard + - goimports - misspell - revive - disable: - # Disable soon to deprecated[1] linters that lead to false - # positives when build tags disable certain files[2] - # 1: https://github.com/golangci/golangci-lint/issues/1841 - # 2: https://github.com/prometheus/node_exporter/issues/1545 - - deadcode - - unused - - structcheck - - varcheck issues: exclude-rules: @@ -32,8 +24,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 f270f78a..542f51e2 100644 --- a/.promu-cgo.yml +++ b/.promu-cgo.yml @@ -1,7 +1,7 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu.yml should also be updated. - version: 1.22 + version: 1.23 cgo: true repository: path: github.com/prometheus/node_exporter diff --git a/.promu.yml b/.promu.yml index 8f883591..fcba92d0 100644 --- a/.promu.yml +++ b/.promu.yml @@ -1,7 +1,7 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu-cgo.yml should also be updated. - version: 1.22 + version: 1.23 repository: path: github.com/prometheus/node_exporter build: diff --git a/.yamllint b/.yamllint index 1859cb62..8d09c375 100644 --- a/.yamllint +++ b/.yamllint @@ -1,7 +1,7 @@ --- extends: default ignore: | - ui/react-app/node_modules + **/node_modules rules: braces: diff --git a/Makefile b/Makefile index 0edd9a12..0b2ff6f2 100644 --- a/Makefile +++ b/Makefile @@ -113,9 +113,13 @@ update_fixtures: rm -vf collector/fixtures/udev/.unpacked ./ttar -C collector/fixtures -c -f collector/fixtures/udev.ttar udev +.PHONY: tools +tools: + @rm ./tools/tools >/dev/null 2>&1 || true + @$(GO) build -o tools ./tools/... .PHONY: test-e2e -test-e2e: build collector/fixtures/sys/.unpacked collector/fixtures/udev/.unpacked +test-e2e: build collector/fixtures/sys/.unpacked collector/fixtures/udev/.unpacked tools @echo ">> running end-to-end tests" ./end-to-end-test.sh diff --git a/Makefile.common b/Makefile.common index e3da72ab..d1576bb3 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.59.1 +GOLANGCI_LINT_VERSION ?= v1.63.4 # 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)) @@ -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 afe496fd..ff5d27ea 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,8 @@ cpu | flags | --collector.cpu.info.flags-include | N/A diskstats | device | --collector.diskstats.device-include | --collector.diskstats.device-exclude ethtool | device | --collector.ethtool.device-include | --collector.ethtool.device-exclude 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 +filesystem | fs-types | --collector.filesystem.fs-types-include | --collector.filesystem.fs-types-exclude +filesystem | mount-points | --collector.filesystem.mount-points-include | --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 @@ -339,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. diff --git a/collector/arp_linux.go b/collector/arp_linux.go index c7861156..e1ba52c8 100644 --- a/collector/arp_linux.go +++ b/collector/arp_linux.go @@ -17,13 +17,11 @@ package collector import ( - "errors" "fmt" - "net" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/jsimonetti/rtnetlink" + "github.com/jsimonetti/rtnetlink/v2/rtnl" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" "golang.org/x/sys/unix" @@ -39,7 +37,7 @@ type arpCollector struct { fs procfs.FS deviceFilter deviceFilter entries *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -47,7 +45,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) @@ -76,44 +74,30 @@ func getTotalArpEntries(deviceEntries []procfs.ARPEntry) map[string]uint32 { } func getTotalArpEntriesRTNL() (map[string]uint32, error) { - conn, err := rtnetlink.Dial(nil) + conn, err := rtnl.Dial(nil) if err != nil { return nil, err } defer conn.Close() - neighbors, err := conn.Neigh.List() + // Neighbors will also contain IPv6 neighbors, but since this is purely an ARP collector, + // restrict to AF_INET. + neighbors, err := conn.Neighbours(nil, unix.AF_INET) if err != nil { return nil, err } - ifIndexEntries := make(map[uint32]uint32) + // Map of interface name to ARP neighbor count. + entries := make(map[string]uint32) for _, n := range neighbors { - // Neighbors will also contain IPv6 neighbors, but since this is purely an ARP collector, - // restrict to AF_INET. Also skip entries which have state NUD_NOARP to conform to output - // of /proc/net/arp. - if n.Family == unix.AF_INET && n.State&unix.NUD_NOARP == 0 { - ifIndexEntries[n.Index]++ + // Skip entries which have state NUD_NOARP to conform to output of /proc/net/arp. + if n.State&unix.NUD_NOARP == 0 { + entries[n.Interface.Name]++ } } - enumEntries := make(map[string]uint32) - - // Convert interface indexes to names. - for ifIndex, entryCount := range ifIndexEntries { - iface, err := net.InterfaceByIndex(int(ifIndex)) - if err != nil { - if errors.Unwrap(err).Error() == "no such network interface" { - continue - } - return nil, err - } - - enumEntries[iface.Name] = entryCount - } - - return enumEntries, nil + return entries, nil } func (c *arpCollector) Update(ch chan<- prometheus.Metric) error { 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 1e5ef293..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 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 75d2a795..8ca70365 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -17,7 +17,9 @@ package collector import ( + "errors" "fmt" + "log/slog" "os" "path/filepath" "regexp" @@ -25,17 +27,17 @@ import ( "strconv" "sync" + "golang.org/x/exp/maps" + "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" ) type cpuCollector struct { - fs procfs.FS + procfs procfs.FS + sysfs sysfs.FS cpu *prometheus.Desc cpuInfo *prometheus.Desc cpuFrequencyHz *prometheus.Desc @@ -45,7 +47,8 @@ type cpuCollector struct { cpuCoreThrottle *prometheus.Desc cpuPackageThrottle *prometheus.Desc cpuIsolated *prometheus.Desc - logger log.Logger + logger *slog.Logger + cpuOnline *prometheus.Desc cpuStats map[int64]procfs.CPUStat cpuStatsMutex sync.Mutex isolatedCpus []uint16 @@ -70,28 +73,29 @@ func init() { } // NewCPUCollector returns a new Collector exposing kernel/system statistics. -func NewCPUCollector(logger log.Logger) (Collector, error) { - fs, err := procfs.NewFS(*procPath) +func NewCPUCollector(logger *slog.Logger) (Collector, error) { + pfs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) } - sysfs, err := sysfs.NewFS(*sysPath) + sfs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) } - isolcpus, err := sysfs.IsolatedCPUs() + isolcpus, err := sfs.IsolatedCPUs() if err != nil { 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{ - fs: fs, - cpu: nodeCPUSecondsDesc, + procfs: pfs, + sysfs: sfs, + cpu: nodeCPUSecondsDesc, cpuInfo: prometheus.NewDesc( prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "info"), "CPU information from /proc/cpuinfo.", @@ -132,6 +136,11 @@ func NewCPUCollector(logger log.Logger) (Collector, error) { "Whether each core is isolated, information from /sys/devices/system/cpu/isolated.", []string{"cpu"}, nil, ), + cpuOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "online"), + "CPUs that are online and being scheduled.", + []string{"cpu"}, nil, + ), logger: logger, isolatedCpus: isolcpus, cpuStats: make(map[int64]procfs.CPUStat), @@ -146,7 +155,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 @@ -178,12 +187,21 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { if c.isolatedCpus != nil { c.updateIsolated(ch) } - return c.updateThermalThrottle(ch) + err := c.updateThermalThrottle(ch) + if err != nil { + return err + } + err = c.updateOnline(ch) + if err != nil { + return err + } + + return nil } // updateInfo reads /proc/cpuinfo func (c *cpuCollector) updateInfo(ch chan<- prometheus.Metric) error { - info, err := c.fs.CPUInfo() + info, err := c.procfs.CPUInfo() if err != nil { return err } @@ -205,7 +223,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 +287,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 +310,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 +320,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) } } } @@ -334,9 +352,31 @@ func (c *cpuCollector) updateIsolated(ch chan<- prometheus.Metric) { } } +// updateOnline reads /sys/devices/system/cpu/cpu*/online through sysfs and exports online status metrics. +func (c *cpuCollector) updateOnline(ch chan<- prometheus.Metric) error { + cpus, err := c.sysfs.CPUs() + if err != nil { + return err + } + // No-op if the system does not support CPU online stats. + cpu0 := cpus[0] + if _, err := cpu0.Online(); err != nil && errors.Is(err, os.ErrNotExist) { + return nil + } + for _, cpu := range cpus { + setOnline := float64(0) + if online, _ := cpu.Online(); online { + setOnline = 1 + } + ch <- prometheus.MustNewConstMetric(c.cpuOnline, prometheus.GaugeValue, setOnline, cpu.Number()) + } + + return nil +} + // updateStat reads /proc/stat through procfs and exports CPU-related metrics. func (c *cpuCollector) updateStat(ch chan<- prometheus.Metric) error { - stats, err := c.fs.Stat() + stats, err := c.procfs.Stat() if err != nil { return err } @@ -380,68 +420,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 1cc25590..ae1235eb 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" @@ -217,7 +216,7 @@ func getCPUTimes() ([]cputime, error) { type statCollector struct { cpu typedDesc temp typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -225,7 +224,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( @@ -272,7 +271,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 c7884542..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, 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..79e2308c 100644 --- a/collector/cpufreq_linux.go +++ b/collector/cpufreq_linux.go @@ -18,15 +18,16 @@ package collector import ( "fmt" - "github.com/go-kit/log" + "log/slog" + "strings" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" - "strings" ) type cpuFreqCollector struct { fs sysfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +35,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..c083a3f0 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 { @@ -113,7 +112,7 @@ func NewDiskstatsCollector(logger log.Logger) (Collector, error) { infoDesc: typedFactorDesc{ desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "info"), "Info of /sys/block/.", - []string{"device", "major", "minor", "path", "wwn", "model", "serial", "revision"}, + []string{"device", "major", "minor", "path", "wwn", "model", "serial", "revision", "rotational"}, nil, ), valueType: prometheus.GaugeValue, }, @@ -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. @@ -295,6 +294,12 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { serial = info[udevIDSerialShort] } + queueStats, err := c.fs.SysBlockDeviceQueueStats(dev) + // Block Device Queue stats may not exist for all devices. + if err != nil && !os.IsNotExist(err) { + c.logger.Debug("Failed to get block device queue stats", "device", dev, "err", err) + } + ch <- c.infoDesc.mustNewConstMetric(1.0, dev, fmt.Sprint(stats.MajorNumber), fmt.Sprint(stats.MinorNumber), @@ -303,6 +308,7 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { info[udevIDModel], serial, info[udevIDRevision], + strconv.FormatUint(queueStats.Rotational, 2), ) statCount := stats.IoStatsCount - 3 // Total diskstats record count, less MajorNumber, MinorNumber and DeviceName @@ -355,14 +361,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 88d8c826..fd90353f 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 @@ -113,21 +113,21 @@ node_disk_flush_requests_time_seconds_total{device="sdc"} 1.944 node_disk_flush_requests_total{device="sdc"} 1555 # HELP node_disk_info Info of /sys/block/. # TYPE node_disk_info gauge -node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 -node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 -node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 -node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 -node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",serial="AAAABBBBCCCC1",wwn=""} 1 -node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",serial="",wwn=""} 1 +node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",rotational="0",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 +node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",rotational="1",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 +node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",rotational="0",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 +node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",rotational="0",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 +node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",rotational="0",serial="AAAABBBBCCCC1",wwn=""} 1 +node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",rotational="0",serial="",wwn=""} 1 # HELP node_disk_io_now The number of I/Os currently in progress. # TYPE node_disk_io_now gauge node_disk_io_now{device="dm-0"} 0 @@ -317,7 +317,7 @@ 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 { t.Fatal(err) 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 45c35851..78f7afe2 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) @@ -97,13 +96,13 @@ func makeEthtoolCollector(logger log.Logger) (*ethtoolCollector, error) { } if *ethtoolDeviceInclude != "" { - level.Info(logger).Log("msg", "Parsed flag --collector.ethtool.device-include", "flag", *ethtoolDeviceInclude) + logger.Info("Parsed flag --collector.ethtool.device-include", "flag", *ethtoolDeviceInclude) } if *ethtoolDeviceExclude != "" { - level.Info(logger).Log("msg", "Parsed flag --collector.ethtool.device-exclude", "flag", *ethtoolDeviceExclude) + logger.Info("Parsed flag --collector.ethtool.device-exclude", "flag", *ethtoolDeviceExclude) } if *ethtoolIncludedMetrics != "" { - level.Info(logger).Log("msg", "Parsed flag --collector.ethtool.metrics-include", "flag", *ethtoolIncludedMetrics) + logger.Info("Parsed flag --collector.ethtool.metrics-include", "flag", *ethtoolIncludedMetrics) } // Pre-populate some common ethtool metrics. @@ -223,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) } @@ -373,10 +372,10 @@ func (c *ethtoolCollector) updateSpeeds(ch chan<- prometheus.Metric, prefix stri } func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { - netClass, err := c.fs.NetClass() + netClass, err := c.fs.NetClassDevices() 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) @@ -386,7 +385,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error { return fmt.Errorf("no network devices found") } - for device := range netClass { + for _, device := range netClass { var stats map[string]uint64 var err error @@ -405,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) } } @@ -422,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) } } @@ -438,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) } } @@ -462,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 ae7df0dd..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,7 +256,7 @@ 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) if err != nil { return nil, err @@ -370,7 +371,7 @@ 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 { t.Fatal(err) 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 782ad33e..cb8be9db 100644 --- a/collector/fibrechannel_linux.go +++ b/collector/fibrechannel_linux.go @@ -18,10 +18,9 @@ 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" @@ -32,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 } @@ -41,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 @@ -99,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) 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..a1314a0f --- /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.mountPointFilter.ignored(stat.MountPoint) { + c.logger.Debug("Ignoring mount point", "mountpoint", stat.MountPoint) + continue + } + fstype := stat.TypeString() + if c.fsTypeFilter.ignored(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..2810a5a3 100644 --- a/collector/filesystem_bsd.go +++ b/collector/filesystem_bsd.go @@ -11,17 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (darwin || dragonfly) && !nofilesystem -// +build darwin dragonfly -// +build !nofilesystem +//go:build dragonfly && !nofilesystem +// +build dragonfly,!nofilesystem package collector import ( "errors" "unsafe" - - "github.com/go-kit/log/level" ) /* @@ -50,15 +47,15 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { stats = []filesystemStats{} 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) + if c.mountPointFilter.ignored(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) + if c.fsTypeFilter.ignored(fstype) { + c.logger.Debug("Ignoring fs type", "type", fstype) continue } diff --git a/collector/filesystem_common.go b/collector/filesystem_common.go index d224e2c2..272366b3 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" - "regexp" + "fmt" + "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -37,7 +36,7 @@ var ( mountPointsExcludeSet bool mountPointsExclude = kingpin.Flag( "collector.filesystem.mount-points-exclude", - "Regexp of mount points to exclude for filesystem collector.", + "Regexp of mount points to exclude for filesystem collector. (mutually exclusive to mount-points-include)", ).Default(defMountPointsExcluded).PreAction(func(c *kingpin.ParseContext) error { mountPointsExcludeSet = true return nil @@ -46,11 +45,15 @@ var ( "collector.filesystem.ignored-mount-points", "Regexp of mount points to ignore for filesystem collector.", ).Hidden().String() + mountPointsInclude = kingpin.Flag( + "collector.filesystem.mount-points-include", + "Regexp of mount points to include for filesystem collector. (mutually exclusive to mount-points-exclude)", + ).String() fsTypesExcludeSet bool fsTypesExclude = kingpin.Flag( "collector.filesystem.fs-types-exclude", - "Regexp of filesystem types to exclude for filesystem collector.", + "Regexp of filesystem types to exclude for filesystem collector. (mutually exclusive to fs-types-include)", ).Default(defFSTypesExcluded).PreAction(func(c *kingpin.ParseContext) error { fsTypesExcludeSet = true return nil @@ -59,18 +62,23 @@ var ( "collector.filesystem.ignored-fs-types", "Regexp of filesystem types to ignore for filesystem collector.", ).Hidden().String() + fsTypesInclude = kingpin.Flag( + "collector.filesystem.fs-types-include", + "Regexp of filesystem types to exclude for filesystem collector. (mutually exclusive to fs-types-exclude)", + ).String() filesystemLabelNames = []string{"device", "mountpoint", "fstype", "device_error"} ) type filesystemCollector struct { - excludedMountPointsPattern *regexp.Regexp - excludedFSTypesPattern *regexp.Regexp + mountPointFilter deviceFilter + fsTypeFilter deviceFilter sizeDesc, freeDesc, availDesc *prometheus.Desc filesDesc, filesFreeDesc *prometheus.Desc + purgeableDesc *prometheus.Desc roDesc, deviceErrorDesc *prometheus.Desc mountInfoDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger } type filesystemLabels struct { @@ -81,6 +89,7 @@ type filesystemStats struct { labels filesystemLabels size, free, avail float64 files, filesFree float64 + purgeable *float64 ro, deviceError float64 } @@ -89,30 +98,8 @@ func init() { } // NewFilesystemCollector returns a new Collector exposing filesystems stats. -func NewFilesystemCollector(logger log.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") - *mountPointsExclude = *oldMountPointsExcluded - } else { - return nil, errors.New("--collector.filesystem.ignored-mount-points and --collector.filesystem.mount-points-exclude are mutually exclusive") - } - } - - 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") - *fsTypesExclude = *oldFSTypesExcluded - } else { - return nil, errors.New("--collector.filesystem.ignored-fs-types and --collector.filesystem.fs-types-exclude are mutually exclusive") - } - } - - subsystem := "filesystem" - level.Info(logger).Log("msg", "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) - filesystemsTypesPattern := regexp.MustCompile(*fsTypesExclude) +func NewFilesystemCollector(logger *slog.Logger) (Collector, error) { + const subsystem = "filesystem" sizeDesc := prometheus.NewDesc( prometheus.BuildFQName(namespace, subsystem, "size_bytes"), @@ -144,6 +131,12 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { filesystemLabelNames, nil, ) + purgeableDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "purgeable_bytes"), + "Filesystem space available including purgeable space (MacOS specific).", + filesystemLabelNames, nil, + ) + roDesc := prometheus.NewDesc( prometheus.BuildFQName(namespace, subsystem, "readonly"), "Filesystem read-only status.", @@ -163,18 +156,29 @@ func NewFilesystemCollector(logger log.Logger) (Collector, error) { nil, ) + mountPointFilter, err := newMountPointsFilter(logger) + if err != nil { + return nil, fmt.Errorf("unable to parse mount points filter flags: %w", err) + } + + fsTypeFilter, err := newFSTypeFilter(logger) + if err != nil { + return nil, fmt.Errorf("unable to parse fs types filter flags: %w", err) + } + return &filesystemCollector{ - excludedMountPointsPattern: mountPointPattern, - excludedFSTypesPattern: filesystemsTypesPattern, - sizeDesc: sizeDesc, - freeDesc: freeDesc, - availDesc: availDesc, - filesDesc: filesDesc, - filesFreeDesc: filesFreeDesc, - roDesc: roDesc, - deviceErrorDesc: deviceErrorDesc, - mountInfoDesc: mountInfoDesc, - logger: logger, + mountPointFilter: mountPointFilter, + fsTypeFilter: fsTypeFilter, + sizeDesc: sizeDesc, + freeDesc: freeDesc, + availDesc: availDesc, + filesDesc: filesDesc, + filesFreeDesc: filesFreeDesc, + purgeableDesc: purgeableDesc, + roDesc: roDesc, + deviceErrorDesc: deviceErrorDesc, + mountInfoDesc: mountInfoDesc, + logger: logger, }, nil } @@ -228,6 +232,71 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error { c.mountInfoDesc, prometheus.GaugeValue, 1.0, s.labels.device, s.labels.major, s.labels.minor, s.labels.mountPoint, ) + if s.purgeable != nil { + ch <- prometheus.MustNewConstMetric( + c.purgeableDesc, prometheus.GaugeValue, + *s.purgeable, s.labels.device, s.labels.mountPoint, + s.labels.fsType, s.labels.deviceError, + ) + } } return nil } + +func newMountPointsFilter(logger *slog.Logger) (deviceFilter, error) { + if *oldMountPointsExcluded != "" { + if !mountPointsExcludeSet { + 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 deviceFilter{}, errors.New("--collector.filesystem.ignored-mount-points and --collector.filesystem.mount-points-exclude are mutually exclusive") + } + } + + if *mountPointsInclude != "" && !mountPointsExcludeSet { + logger.Debug("mount-points-exclude flag not set when mount-points-include flag is set, assuming include is desired") + *mountPointsExclude = "" + } + + if *mountPointsExclude != "" && *mountPointsInclude != "" { + return deviceFilter{}, errors.New("--collector.filesystem.mount-points-exclude and --collector.filesystem.mount-points-include are mutually exclusive") + } + + if *mountPointsExclude != "" { + logger.Info("Parsed flag --collector.filesystem.mount-points-exclude", "flag", *mountPointsExclude) + } + if *mountPointsInclude != "" { + logger.Info("Parsed flag --collector.filesystem.mount-points-include", "flag", *mountPointsInclude) + } + + return newDeviceFilter(*mountPointsExclude, *mountPointsInclude), nil +} + +func newFSTypeFilter(logger *slog.Logger) (deviceFilter, error) { + if *oldFSTypesExcluded != "" { + if !fsTypesExcludeSet { + 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 deviceFilter{}, errors.New("--collector.filesystem.ignored-fs-types and --collector.filesystem.fs-types-exclude are mutually exclusive") + } + } + + if *fsTypesInclude != "" && !fsTypesExcludeSet { + logger.Debug("fs-types-exclude flag not set when fs-types-include flag is set, assuming include is desired") + *fsTypesExclude = "" + } + + if *fsTypesExclude != "" && *fsTypesInclude != "" { + return deviceFilter{}, errors.New("--collector.filesystem.fs-types-exclude and --collector.filesystem.fs-types-include are mutually exclusive") + } + + if *fsTypesExclude != "" { + logger.Info("Parsed flag --collector.filesystem.fs-types-exclude", "flag", *fsTypesExclude) + } + if *fsTypesInclude != "" { + logger.Info("Parsed flag --collector.filesystem.fs-types-include", "flag", *fsTypesInclude) + } + + return newDeviceFilter(*fsTypesExclude, *fsTypesInclude), nil +} diff --git a/collector/filesystem_freebsd.go b/collector/filesystem_freebsd.go index f05702d1..502ae0a8 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" ) @@ -40,20 +39,20 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { stats := []filesystemStats{} 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) + if c.mountPointFilter.ignored(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) + if c.fsTypeFilter.ignored(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 728e8cca..3a7bda4d 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" ) @@ -74,12 +73,12 @@ 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) + if c.mountPointFilter.ignored(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) + if c.fsTypeFilter.ignored(labels.fsType) { + c.logger.Debug("Ignoring fs type", "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,18 +169,18 @@ 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) { +func mountPointDetails(logger *slog.Logger) ([]filesystemLabels, error) { file, err := os.Open(procFilePath("1/mountinfo")) if errors.Is(err, os.ErrNotExist) { // Fallback to `/proc/self/mountinfo` if `/proc/1/mountinfo` is missing due hidepid. - level.Debug(logger).Log("msg", "Reading root mounts failed, falling back to self mounts", "err", err) + logger.Debug("Reading root mounts failed, falling back to self mounts", "err", err) file, err = os.Open(procFilePath("self/mountinfo")) } if err != nil { diff --git a/collector/filesystem_linux_test.go b/collector/filesystem_linux_test.go index e2938552..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,7 +83,7 @@ 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) } @@ -111,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) } @@ -139,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_macos.go b/collector/filesystem_macos.go new file mode 100644 index 00000000..0894cabc --- /dev/null +++ b/collector/filesystem_macos.go @@ -0,0 +1,108 @@ +// Copyright 2015 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 darwin && !nofilesystem +// +build darwin,!nofilesystem + +package collector + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#import +Float64 *purgeable(char *path) { + CFNumberRef tmp; + Float64 *value; + NSError *error = nil; + NSString *str = [NSString stringWithUTF8String:path]; + NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:str]; + NSDictionary *results = [fileURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error]; + if (results) { + if ((tmp = CFDictionaryGetValue((CFDictionaryRef)results, NSURLVolumeAvailableCapacityForImportantUsageKey)) == NULL) + return NULL; + value = (Float64 *)malloc(sizeof(Float64)); + if (CFNumberGetValue(tmp, kCFNumberFloat64Type, value)) { + return value; + } + } + free(value); + return NULL; +} +*/ +import "C" + +import ( + "errors" + "unsafe" +) + +/* +#include +#include +#include +#include +*/ +import "C" + +const ( + defMountPointsExcluded = "^/(dev)($|/)" + defFSTypesExcluded = "^devfs$" + readOnly = 0x1 // MNT_RDONLY +) + +// Expose filesystem fullness. +func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { + var mntbuf *C.struct_statfs + count := C.getmntinfo(&mntbuf, C.MNT_NOWAIT) + if count == 0 { + return nil, errors.New("getmntinfo() failed") + } + + mnt := (*[1 << 20]C.struct_statfs)(unsafe.Pointer(mntbuf)) + stats = []filesystemStats{} + for i := 0; i < int(count); i++ { + mountpoint := C.GoString(&mnt[i].f_mntonname[0]) + if c.mountPointFilter.ignored(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.fsTypeFilter.ignored(fstype) { + c.logger.Debug("Ignoring fs type", "type", fstype) + continue + } + + var ro float64 + if (mnt[i].f_flags & readOnly) != 0 { + ro = 1 + } + + stats = append(stats, filesystemStats{ + labels: filesystemLabels{ + device: device, + mountPoint: rootfsStripPrefix(mountpoint), + fsType: fstype, + }, + size: float64(mnt[i].f_blocks) * float64(mnt[i].f_bsize), + free: float64(mnt[i].f_bfree) * float64(mnt[i].f_bsize), + avail: float64(mnt[i].f_bavail) * float64(mnt[i].f_bsize), + files: float64(mnt[i].f_files), + filesFree: float64(mnt[i].f_ffree), + purgeable: (*float64)(C.purgeable(C.CString(mountpoint))), + ro: ro, + }) + } + return stats, nil +} diff --git a/collector/filesystem_netbsd.go b/collector/filesystem_netbsd.go new file mode 100644 index 00000000..c7395893 --- /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.mountPointFilter.ignored(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.fsTypeFilter.ignored(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..fa9f8eb8 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" ) @@ -42,15 +41,15 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { stats = []filesystemStats{} 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) + if c.mountPointFilter.ignored(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) + if c.fsTypeFilter.ignored(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 d649896a..030fa88e 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 @@ -484,21 +488,21 @@ node_disk_flush_requests_time_seconds_total{device="sdc"} 1.944 node_disk_flush_requests_total{device="sdc"} 1555 # HELP node_disk_info Info of /sys/block/. # TYPE node_disk_info gauge -node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 -node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 -node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 -node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 -node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",serial="AAAABBBBCCCC1",wwn=""} 1 -node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",serial="",wwn=""} 1 +node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",rotational="0",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 +node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",rotational="1",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 +node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",rotational="0",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 +node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",rotational="0",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 +node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",rotational="0",serial="AAAABBBBCCCC1",wwn=""} 1 +node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",rotational="0",serial="",wwn=""} 1 # HELP node_disk_io_now The number of I/Os currently in progress. # TYPE node_disk_io_now gauge node_disk_io_now{device="dm-0"} 0 @@ -867,6 +871,10 @@ node_hwmon_fan_target_rpm{chip="nct6779",sensor="fan2"} 27000 # HELP node_hwmon_fan_tolerance Hardware monitor fan element tolerance # TYPE node_hwmon_fan_tolerance gauge node_hwmon_fan_tolerance{chip="nct6779",sensor="fan2"} 0 +# HELP node_hwmon_freq_freq_mhz Hardware monitor for GPU frequency in MHz +# TYPE node_hwmon_freq_freq_mhz gauge +node_hwmon_freq_freq_mhz{chip="hwmon4",sensor="mclk"} 300 +node_hwmon_freq_freq_mhz{chip="hwmon4",sensor="sclk"} 214 # HELP node_hwmon_in_alarm Hardware sensor alarm status (in) # TYPE node_hwmon_in_alarm gauge node_hwmon_in_alarm{chip="nct6779",sensor="in0"} 0 @@ -980,8 +988,10 @@ node_hwmon_pwm_weight_temp_step_tol{chip="nct6779",sensor="pwm1"} 0 # TYPE node_hwmon_sensor_label gauge node_hwmon_sensor_label{chip="hwmon4",label="foosensor",sensor="temp1"} 1 node_hwmon_sensor_label{chip="hwmon4",label="foosensor",sensor="temp2"} 1 -node_hwmon_sensor_label{chip="platform_applesmc_768",label="Left side ",sensor="fan1"} 1 -node_hwmon_sensor_label{chip="platform_applesmc_768",label="Right side ",sensor="fan2"} 1 +node_hwmon_sensor_label{chip="hwmon4",label="mclk",sensor="freq2"} 1 +node_hwmon_sensor_label{chip="hwmon4",label="sclk",sensor="freq1"} 1 +node_hwmon_sensor_label{chip="platform_applesmc_768",label="Left side",sensor="fan1"} 1 +node_hwmon_sensor_label{chip="platform_applesmc_768",label="Right side",sensor="fan2"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 0",sensor="temp2"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 1",sensor="temp3"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 2",sensor="temp4"} 1 @@ -4311,108 +4321,139 @@ node_zfs_zil_zil_itx_needcopy_count 0 # TYPE node_zfs_zpool_dataset_nread untyped node_zfs_zpool_dataset_nread{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nread{dataset="pool1/dataset1",zpool="pool1"} 28 +node_zfs_zpool_dataset_nread{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nread{dataset="pool3/dataset with space",zpool="pool3"} 28 node_zfs_zpool_dataset_nread{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nread{dataset="poolz1/dataset1",zpool="poolz1"} 28 # HELP node_zfs_zpool_dataset_nunlinked kstat.zfs.misc.objset.nunlinked # TYPE node_zfs_zpool_dataset_nunlinked untyped node_zfs_zpool_dataset_nunlinked{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nunlinked{dataset="pool1/dataset1",zpool="pool1"} 3 +node_zfs_zpool_dataset_nunlinked{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nunlinked{dataset="pool3/dataset with space",zpool="pool3"} 3 node_zfs_zpool_dataset_nunlinked{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nunlinked{dataset="poolz1/dataset1",zpool="poolz1"} 14 # HELP node_zfs_zpool_dataset_nunlinks kstat.zfs.misc.objset.nunlinks # TYPE node_zfs_zpool_dataset_nunlinks untyped node_zfs_zpool_dataset_nunlinks{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nunlinks{dataset="pool1/dataset1",zpool="pool1"} 3 +node_zfs_zpool_dataset_nunlinks{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nunlinks{dataset="pool3/dataset with space",zpool="pool3"} 3 node_zfs_zpool_dataset_nunlinks{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nunlinks{dataset="poolz1/dataset1",zpool="poolz1"} 14 # HELP node_zfs_zpool_dataset_nwritten kstat.zfs.misc.objset.nwritten # TYPE node_zfs_zpool_dataset_nwritten untyped node_zfs_zpool_dataset_nwritten{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nwritten{dataset="pool1/dataset1",zpool="pool1"} 12302 +node_zfs_zpool_dataset_nwritten{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nwritten{dataset="pool3/dataset with space",zpool="pool3"} 12302 node_zfs_zpool_dataset_nwritten{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nwritten{dataset="poolz1/dataset1",zpool="poolz1"} 32806 # HELP node_zfs_zpool_dataset_reads kstat.zfs.misc.objset.reads # TYPE node_zfs_zpool_dataset_reads untyped node_zfs_zpool_dataset_reads{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_reads{dataset="pool1/dataset1",zpool="pool1"} 2 +node_zfs_zpool_dataset_reads{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_reads{dataset="pool3/dataset with space",zpool="pool3"} 2 node_zfs_zpool_dataset_reads{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_reads{dataset="poolz1/dataset1",zpool="poolz1"} 2 # HELP node_zfs_zpool_dataset_writes kstat.zfs.misc.objset.writes # TYPE node_zfs_zpool_dataset_writes untyped node_zfs_zpool_dataset_writes{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_writes{dataset="pool1/dataset1",zpool="pool1"} 4 +node_zfs_zpool_dataset_writes{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_writes{dataset="pool3/dataset with space",zpool="pool3"} 4 node_zfs_zpool_dataset_writes{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_writes{dataset="poolz1/dataset1",zpool="poolz1"} 10 # HELP node_zfs_zpool_nread kstat.zfs.misc.io.nread # TYPE node_zfs_zpool_nread untyped node_zfs_zpool_nread{zpool="pool1"} 1.88416e+06 +node_zfs_zpool_nread{zpool="pool3"} 1.88416e+06 node_zfs_zpool_nread{zpool="poolz1"} 2.82624e+06 # HELP node_zfs_zpool_nwritten kstat.zfs.misc.io.nwritten # TYPE node_zfs_zpool_nwritten untyped node_zfs_zpool_nwritten{zpool="pool1"} 3.206144e+06 +node_zfs_zpool_nwritten{zpool="pool3"} 3.206144e+06 node_zfs_zpool_nwritten{zpool="poolz1"} 2.680501248e+09 # HELP node_zfs_zpool_rcnt kstat.zfs.misc.io.rcnt # TYPE node_zfs_zpool_rcnt untyped node_zfs_zpool_rcnt{zpool="pool1"} 0 +node_zfs_zpool_rcnt{zpool="pool3"} 0 node_zfs_zpool_rcnt{zpool="poolz1"} 0 # HELP node_zfs_zpool_reads kstat.zfs.misc.io.reads # TYPE node_zfs_zpool_reads untyped node_zfs_zpool_reads{zpool="pool1"} 22 +node_zfs_zpool_reads{zpool="pool3"} 22 node_zfs_zpool_reads{zpool="poolz1"} 33 # HELP node_zfs_zpool_rlentime kstat.zfs.misc.io.rlentime # TYPE node_zfs_zpool_rlentime untyped node_zfs_zpool_rlentime{zpool="pool1"} 1.04112268e+08 +node_zfs_zpool_rlentime{zpool="pool3"} 1.04112268e+08 node_zfs_zpool_rlentime{zpool="poolz1"} 6.472105124093e+12 # HELP node_zfs_zpool_rtime kstat.zfs.misc.io.rtime # TYPE node_zfs_zpool_rtime untyped node_zfs_zpool_rtime{zpool="pool1"} 2.4168078e+07 +node_zfs_zpool_rtime{zpool="pool3"} 2.4168078e+07 node_zfs_zpool_rtime{zpool="poolz1"} 9.82909164e+09 # HELP node_zfs_zpool_rupdate kstat.zfs.misc.io.rupdate # TYPE node_zfs_zpool_rupdate untyped node_zfs_zpool_rupdate{zpool="pool1"} 7.921048984922e+13 +node_zfs_zpool_rupdate{zpool="pool3"} 7.921048984922e+13 node_zfs_zpool_rupdate{zpool="poolz1"} 1.10734831944501e+14 # HELP node_zfs_zpool_state kstat.zfs.misc.state # TYPE node_zfs_zpool_state gauge node_zfs_zpool_state{state="degraded",zpool="pool1"} 0 node_zfs_zpool_state{state="degraded",zpool="pool2"} 0 +node_zfs_zpool_state{state="degraded",zpool="pool3"} 0 node_zfs_zpool_state{state="degraded",zpool="poolz1"} 1 node_zfs_zpool_state{state="faulted",zpool="pool1"} 0 node_zfs_zpool_state{state="faulted",zpool="pool2"} 0 +node_zfs_zpool_state{state="faulted",zpool="pool3"} 0 node_zfs_zpool_state{state="faulted",zpool="poolz1"} 0 node_zfs_zpool_state{state="offline",zpool="pool1"} 0 node_zfs_zpool_state{state="offline",zpool="pool2"} 0 +node_zfs_zpool_state{state="offline",zpool="pool3"} 0 node_zfs_zpool_state{state="offline",zpool="poolz1"} 0 node_zfs_zpool_state{state="online",zpool="pool1"} 1 node_zfs_zpool_state{state="online",zpool="pool2"} 0 +node_zfs_zpool_state{state="online",zpool="pool3"} 1 node_zfs_zpool_state{state="online",zpool="poolz1"} 0 node_zfs_zpool_state{state="removed",zpool="pool1"} 0 node_zfs_zpool_state{state="removed",zpool="pool2"} 0 +node_zfs_zpool_state{state="removed",zpool="pool3"} 0 node_zfs_zpool_state{state="removed",zpool="poolz1"} 0 node_zfs_zpool_state{state="suspended",zpool="pool1"} 0 node_zfs_zpool_state{state="suspended",zpool="pool2"} 1 +node_zfs_zpool_state{state="suspended",zpool="pool3"} 0 node_zfs_zpool_state{state="suspended",zpool="poolz1"} 0 node_zfs_zpool_state{state="unavail",zpool="pool1"} 0 node_zfs_zpool_state{state="unavail",zpool="pool2"} 0 +node_zfs_zpool_state{state="unavail",zpool="pool3"} 0 node_zfs_zpool_state{state="unavail",zpool="poolz1"} 0 # HELP node_zfs_zpool_wcnt kstat.zfs.misc.io.wcnt # TYPE node_zfs_zpool_wcnt untyped node_zfs_zpool_wcnt{zpool="pool1"} 0 +node_zfs_zpool_wcnt{zpool="pool3"} 0 node_zfs_zpool_wcnt{zpool="poolz1"} 0 # HELP node_zfs_zpool_wlentime kstat.zfs.misc.io.wlentime # TYPE node_zfs_zpool_wlentime untyped node_zfs_zpool_wlentime{zpool="pool1"} 1.04112268e+08 +node_zfs_zpool_wlentime{zpool="pool3"} 1.04112268e+08 node_zfs_zpool_wlentime{zpool="poolz1"} 6.472105124093e+12 # HELP node_zfs_zpool_writes kstat.zfs.misc.io.writes # TYPE node_zfs_zpool_writes untyped node_zfs_zpool_writes{zpool="pool1"} 132 +node_zfs_zpool_writes{zpool="pool3"} 132 node_zfs_zpool_writes{zpool="poolz1"} 25294 # HELP node_zfs_zpool_wtime kstat.zfs.misc.io.wtime # TYPE node_zfs_zpool_wtime untyped node_zfs_zpool_wtime{zpool="pool1"} 7.155162e+06 +node_zfs_zpool_wtime{zpool="pool3"} 7.155162e+06 node_zfs_zpool_wtime{zpool="poolz1"} 9.673715628e+09 # HELP node_zfs_zpool_wupdate kstat.zfs.misc.io.wupdate # TYPE node_zfs_zpool_wupdate untyped node_zfs_zpool_wupdate{zpool="pool1"} 7.9210489694949e+13 +node_zfs_zpool_wupdate{zpool="pool3"} 7.9210489694949e+13 node_zfs_zpool_wupdate{zpool="poolz1"} 1.10734831833266e+14 # HELP node_zoneinfo_high_pages Zone watermark pages_high # TYPE node_zoneinfo_high_pages gauge @@ -4589,6 +4630,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-darwin.txt b/collector/fixtures/e2e-output-darwin.txt new file mode 100644 index 00000000..77eb45cb --- /dev/null +++ b/collector/fixtures/e2e-output-darwin.txt @@ -0,0 +1,277 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_buddyinfo_blocks Count of free blocks according to size. +# TYPE node_buddyinfo_blocks gauge +node_buddyinfo_blocks{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_blocks{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_blocks{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_blocks{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_blocks{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_blocks{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_blocks{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_blocks{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_blocks{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_blocks{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_blocks{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_blocks{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_blocks{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_blocks{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_blocks{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_blocks{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_blocks{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 +# HELP node_disk_read_errors_total The total number of read errors. +# TYPE node_disk_read_errors_total counter +node_disk_read_errors_total{device="disk0"} 0 +# HELP node_disk_read_retries_total The total number of read retries. +# TYPE node_disk_read_retries_total counter +node_disk_read_retries_total{device="disk0"} 0 +# HELP node_disk_write_errors_total The total number of write errors. +# TYPE node_disk_write_errors_total counter +node_disk_write_errors_total{device="disk0"} 0 +# HELP node_disk_write_retries_total The total number of write retries. +# TYPE node_disk_write_retries_total counter +node_disk_write_retries_total{device="disk0"} 0 +# 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_memory_swap_total_bytes Memory information field swap_total_bytes. +# TYPE node_memory_swap_total_bytes gauge +node_memory_swap_total_bytes 0 +# HELP node_memory_swap_used_bytes Memory information field swap_used_bytes. +# TYPE node_memory_swap_used_bytes gauge +node_memory_swap_used_bytes 0 +# HELP node_memory_total_bytes Memory information field total_bytes. +# TYPE node_memory_total_bytes gauge +node_memory_total_bytes 7.516192768e+09 +# HELP node_network_noproto_total Network device statistic noproto. +# TYPE node_network_noproto_total counter +node_network_noproto_total{device="lo0"} 0 +# HELP node_network_receive_drop_total Network device statistic receive_drop. +# TYPE node_network_receive_drop_total counter +node_network_receive_drop_total{device="lo0"} 0 +# HELP node_network_receive_errs_total Network device statistic receive_errs. +# TYPE node_network_receive_errs_total counter +node_network_receive_errs_total{device="lo0"} 0 +# HELP node_network_receive_packets_total Network device statistic receive_packets. +# TYPE node_network_receive_packets_total counter +# HELP node_network_transmit_bytes_total Network device statistic transmit_bytes. +# TYPE node_network_transmit_bytes_total counter +# HELP node_network_transmit_colls_total Network device statistic transmit_colls. +# TYPE node_network_transmit_colls_total counter +node_network_transmit_colls_total{device="lo0"} 0 +# HELP node_network_transmit_errs_total Network device statistic transmit_errs. +# TYPE node_network_transmit_errs_total counter +node_network_transmit_errs_total{device="lo0"} 0 +# HELP node_network_transmit_packets_total Network device statistic transmit_packets. +# TYPE node_network_transmit_packets_total counter +# HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="buddyinfo"} 1 +node_scrape_collector_success{collector="cpu"} 1 +node_scrape_collector_success{collector="diskstats"} 1 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="meminfo"} 1 +node_scrape_collector_success{collector="netdev"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="powersupplyclass"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="thermal"} 0 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output-dragonfly.txt b/collector/fixtures/e2e-output-dragonfly.txt new file mode 100644 index 00000000..b6a828ad --- /dev/null +++ b/collector/fixtures/e2e-output-dragonfly.txt @@ -0,0 +1,251 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_buddyinfo_blocks Count of free blocks according to size. +# TYPE node_buddyinfo_blocks gauge +node_buddyinfo_blocks{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_blocks{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_blocks{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_blocks{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_blocks{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_blocks{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_blocks{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_blocks{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_blocks{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_blocks{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_blocks{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_blocks{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_blocks{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_blocks{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_blocks{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_blocks{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_blocks{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 +# 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_network_receive_drop_total Network device statistic receive_drop. +# TYPE node_network_receive_drop_total counter +node_network_receive_drop_total{device="lo0"} 0 +# HELP node_network_receive_errs_total Network device statistic receive_errs. +# TYPE node_network_receive_errs_total counter +node_network_receive_errs_total{device="lo0"} 0 +# HELP node_network_receive_packets_total Network device statistic receive_packets. +# TYPE node_network_receive_packets_total counter +# HELP node_network_transmit_bytes_total Network device statistic transmit_bytes. +# TYPE node_network_transmit_bytes_total counter +# HELP node_network_transmit_drop_total Network device statistic transmit_drop. +# TYPE node_network_transmit_drop_total counter +node_network_transmit_drop_total{device="lo0"} 0 +# HELP node_network_transmit_errs_total Network device statistic transmit_errs. +# TYPE node_network_transmit_errs_total counter +node_network_transmit_errs_total{device="lo0"} 0 +# HELP node_network_transmit_packets_total Network device statistic transmit_packets. +# TYPE node_network_transmit_packets_total counter +# HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="buddyinfo"} 1 +node_scrape_collector_success{collector="cpu"} 1 +node_scrape_collector_success{collector="exec"} 1 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="meminfo"} 0 +node_scrape_collector_success{collector="netdev"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output-freebsd.txt b/collector/fixtures/e2e-output-freebsd.txt new file mode 100644 index 00000000..e7b1dd50 --- /dev/null +++ b/collector/fixtures/e2e-output-freebsd.txt @@ -0,0 +1,287 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_buddyinfo_blocks Count of free blocks according to size. +# TYPE node_buddyinfo_blocks gauge +node_buddyinfo_blocks{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_blocks{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_blocks{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_blocks{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_blocks{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_blocks{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_blocks{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_blocks{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_blocks{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_blocks{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_blocks{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_blocks{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_blocks{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_blocks{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_blocks{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_blocks{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_blocks{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 +# 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_memory_swap_in_bytes_total Bytes paged in from swap devices +# TYPE node_memory_swap_in_bytes_total counter +node_memory_swap_in_bytes_total 0 +# HELP node_memory_swap_out_bytes_total Bytes paged out to swap devices +# TYPE node_memory_swap_out_bytes_total counter +node_memory_swap_out_bytes_total 0 +# HELP node_memory_swap_size_bytes Total swap memory size +# TYPE node_memory_swap_size_bytes gauge +node_memory_swap_size_bytes 1.073741824e+09 +# HELP node_memory_swap_used_bytes Currently allocated swap +# TYPE node_memory_swap_used_bytes gauge +node_memory_swap_used_bytes 0 +# HELP node_memory_user_wired_bytes Locked in memory by user, mlock, etc +# TYPE node_memory_user_wired_bytes gauge +node_memory_user_wired_bytes 0 +# HELP node_netisr_bindthreads netisr threads bound to CPUs +# TYPE node_netisr_bindthreads gauge +node_netisr_bindthreads 0 +# HELP node_netisr_defaultqlimit netisr default queue limit +# TYPE node_netisr_defaultqlimit gauge +node_netisr_defaultqlimit 256 +# HELP node_netisr_maxprot netisr maximum protocols +# TYPE node_netisr_maxprot gauge +node_netisr_maxprot 16 +# HELP node_netisr_maxqlimit netisr maximum queue limit +# TYPE node_netisr_maxqlimit gauge +node_netisr_maxqlimit 10240 +# HELP node_netisr_maxthreads netisr maximum thread count +# TYPE node_netisr_maxthreads gauge +node_netisr_maxthreads 1 +# HELP node_netisr_numthreads netisr current thread count +# TYPE node_netisr_numthreads gauge +node_netisr_numthreads 1 +# HELP node_network_receive_drop_total Network device statistic receive_drop. +# TYPE node_network_receive_drop_total counter +node_network_receive_drop_total{device="lo0"} 0 +# HELP node_network_receive_errs_total Network device statistic receive_errs. +# TYPE node_network_receive_errs_total counter +node_network_receive_errs_total{device="lo0"} 0 +# HELP node_network_receive_packets_total Network device statistic receive_packets. +# TYPE node_network_receive_packets_total counter +# HELP node_network_transmit_bytes_total Network device statistic transmit_bytes. +# TYPE node_network_transmit_bytes_total counter +# HELP node_network_transmit_drop_total Network device statistic transmit_drop. +# TYPE node_network_transmit_drop_total counter +node_network_transmit_drop_total{device="lo0"} 0 +# HELP node_network_transmit_errs_total Network device statistic transmit_errs. +# TYPE node_network_transmit_errs_total counter +node_network_transmit_errs_total{device="lo0"} 0 +# HELP node_network_transmit_packets_total Network device statistic transmit_packets. +# TYPE node_network_transmit_packets_total counter +# HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="buddyinfo"} 1 +node_scrape_collector_success{collector="cpu"} 1 +node_scrape_collector_success{collector="exec"} 1 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="meminfo"} 1 +node_scrape_collector_success{collector="netdev"} 1 +node_scrape_collector_success{collector="netisr"} 1 +node_scrape_collector_success{collector="netstat"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +node_scrape_collector_success{collector="zfs"} 1 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output-netbsd.txt b/collector/fixtures/e2e-output-netbsd.txt new file mode 100644 index 00000000..1a6f1402 --- /dev/null +++ b/collector/fixtures/e2e-output-netbsd.txt @@ -0,0 +1,209 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_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_memory_swap_size_bytes Memory information field swap_size_bytes. +# TYPE node_memory_swap_size_bytes gauge +node_memory_swap_size_bytes 6.442426368e+09 +# HELP node_memory_swap_used_bytes Memory information field swap_used_bytes. +# TYPE node_memory_swap_used_bytes gauge +node_memory_swap_used_bytes 0 +# HELP node_memory_swapped_in_pages_bytes_total Memory information field swapped_in_pages_bytes_total. +# TYPE node_memory_swapped_in_pages_bytes_total counter +node_memory_swapped_in_pages_bytes_total 0 +# HELP node_memory_swapped_out_pages_bytes_total Memory information field swapped_out_pages_bytes_total. +# TYPE node_memory_swapped_out_pages_bytes_total counter +node_memory_swapped_out_pages_bytes_total 0 +# HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="cpu"} 0 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="meminfo"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP process_open_fds Number of open file descriptors. +# TYPE process_open_fds gauge +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output-openbsd.txt b/collector/fixtures/e2e-output-openbsd.txt new file mode 100644 index 00000000..96d2109d --- /dev/null +++ b/collector/fixtures/e2e-output-openbsd.txt @@ -0,0 +1,276 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_buddyinfo_blocks Count of free blocks according to size. +# TYPE node_buddyinfo_blocks gauge +node_buddyinfo_blocks{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_blocks{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_blocks{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_blocks{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_blocks{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_blocks{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_blocks{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_blocks{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_blocks{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_blocks{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_blocks{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_blocks{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_blocks{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_blocks{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_blocks{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_blocks{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_blocks{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 +# 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_memory_swap_size_bytes Memory information field swap_size_bytes. +# TYPE node_memory_swap_size_bytes gauge +node_memory_swap_size_bytes 6.693941248e+09 +# HELP node_memory_swap_used_bytes Memory information field swap_used_bytes. +# TYPE node_memory_swap_used_bytes gauge +node_memory_swap_used_bytes 0 +# HELP node_memory_swapped_in_pages_bytes_total Memory information field swapped_in_pages_bytes_total. +# TYPE node_memory_swapped_in_pages_bytes_total counter +node_memory_swapped_in_pages_bytes_total 0 +# HELP node_memory_swapped_out_pages_bytes_total Memory information field swapped_out_pages_bytes_total. +# TYPE node_memory_swapped_out_pages_bytes_total counter +node_memory_swapped_out_pages_bytes_total 0 +# HELP node_network_noproto_total Network device statistic noproto. +# TYPE node_network_noproto_total counter +node_network_noproto_total{device="lo0"} 0 +node_network_noproto_total{device="pflog0"} 0 +# HELP node_network_receive_drop_total Network device statistic receive_drop. +# TYPE node_network_receive_drop_total counter +node_network_receive_drop_total{device="lo0"} 0 +node_network_receive_drop_total{device="pflog0"} 0 +# HELP node_network_receive_errs_total Network device statistic receive_errs. +# TYPE node_network_receive_errs_total counter +node_network_receive_errs_total{device="lo0"} 0 +node_network_receive_errs_total{device="pflog0"} 0 +# HELP node_network_receive_packets_total Network device statistic receive_packets. +# TYPE node_network_receive_packets_total counter +# HELP node_network_transmit_bytes_total Network device statistic transmit_bytes. +# TYPE node_network_transmit_bytes_total counter +# HELP node_network_transmit_colls_total Network device statistic transmit_colls. +# TYPE node_network_transmit_colls_total counter +node_network_transmit_colls_total{device="lo0"} 0 +node_network_transmit_colls_total{device="pflog0"} 0 +# HELP node_network_transmit_drop_total Network device statistic transmit_drop. +# TYPE node_network_transmit_drop_total counter +node_network_transmit_drop_total{device="lo0"} 0 +node_network_transmit_drop_total{device="pflog0"} 0 +# HELP node_network_transmit_errs_total Network device statistic transmit_errs. +# TYPE node_network_transmit_errs_total counter +node_network_transmit_errs_total{device="lo0"} 0 +node_network_transmit_errs_total{device="pflog0"} 0 +# HELP node_network_transmit_packets_total Network device statistic transmit_packets. +# TYPE node_network_transmit_packets_total counter +# HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="buddyinfo"} 1 +node_scrape_collector_success{collector="cpu"} 1 +node_scrape_collector_success{collector="diskstats"} 1 +node_scrape_collector_success{collector="interrupts"} 1 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="meminfo"} 1 +node_scrape_collector_success{collector="netdev"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output-solaris.txt b/collector/fixtures/e2e-output-solaris.txt new file mode 100644 index 00000000..2f39a5f6 --- /dev/null +++ b/collector/fixtures/e2e-output-solaris.txt @@ -0,0 +1,240 @@ +# 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 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 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. Equals to /memory/classes/profiling/buckets:bytes. +# TYPE go_memstats_buck_hash_sys_bytes gauge +# 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. 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 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. 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. 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 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. 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. 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_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. 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. 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. 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. 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. 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. Equals to /memory/classes/other:bytes. +# TYPE go_memstats_other_sys_bytes gauge +# 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. 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. 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_buddyinfo_blocks Count of free blocks according to size. +# TYPE node_buddyinfo_blocks gauge +node_buddyinfo_blocks{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_blocks{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_blocks{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_blocks{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_blocks{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_blocks{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_blocks{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_blocks{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_blocks{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_blocks{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_blocks{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_blocks{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_blocks{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_blocks{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_blocks{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_blocks{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_blocks{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_blocks{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 +# HELP node_cpu_frequency_max_hertz Maximum CPU thread frequency in hertz. +# TYPE node_cpu_frequency_max_hertz gauge +node_cpu_frequency_max_hertz{cpu="0"} 2.445e+09 +node_cpu_frequency_max_hertz{cpu="1"} 2.445e+09 +node_cpu_frequency_max_hertz{cpu="2"} 2.445e+09 +node_cpu_frequency_max_hertz{cpu="3"} 2.445e+09 +# 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_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. +# TYPE node_os_info gauge +node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 +# HELP node_os_version Metric containing the major.minor part of the OS version. +# TYPE node_os_version gauge +node_os_version{id="ubuntu",id_like="debian",name="Ubuntu"} 20.04 +# HELP node_scrape_collector_duration_seconds node_exporter: Duration of a collector scrape. +# TYPE node_scrape_collector_duration_seconds gauge +# HELP node_scrape_collector_success node_exporter: Whether a collector succeeded. +# TYPE node_scrape_collector_success gauge +node_scrape_collector_success{collector="boottime"} 1 +node_scrape_collector_success{collector="buddyinfo"} 1 +node_scrape_collector_success{collector="cpu"} 0 +node_scrape_collector_success{collector="cpufreq"} 1 +node_scrape_collector_success{collector="loadavg"} 1 +node_scrape_collector_success{collector="os"} 1 +node_scrape_collector_success{collector="textfile"} 1 +node_scrape_collector_success{collector="time"} 1 +node_scrape_collector_success{collector="xfrm"} 1 +node_scrape_collector_success{collector="zfs"} 0 +# HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. +# TYPE node_textfile_mtime_seconds gauge +# 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 +# HELP node_time_seconds System time in seconds since epoch (1970). +# TYPE node_time_seconds gauge +# HELP node_time_zone_offset_seconds System time zone offset in seconds. +# TYPE node_time_zone_offset_seconds gauge +# HELP node_xfrm_acquire_error_packets_total State hasn’t been fully acquired before use +# TYPE node_xfrm_acquire_error_packets_total counter +node_xfrm_acquire_error_packets_total 24532 +# HELP node_xfrm_fwd_hdr_error_packets_total Forward routing of a packet is not allowed +# TYPE node_xfrm_fwd_hdr_error_packets_total counter +node_xfrm_fwd_hdr_error_packets_total 6654 +# HELP node_xfrm_in_buffer_error_packets_total No buffer is left +# TYPE node_xfrm_in_buffer_error_packets_total counter +node_xfrm_in_buffer_error_packets_total 2 +# HELP node_xfrm_in_error_packets_total All errors not matched by other +# TYPE node_xfrm_in_error_packets_total counter +node_xfrm_in_error_packets_total 1 +# HELP node_xfrm_in_hdr_error_packets_total Header error +# TYPE node_xfrm_in_hdr_error_packets_total counter +node_xfrm_in_hdr_error_packets_total 4 +# HELP node_xfrm_in_no_pols_packets_total No policy is found for states e.g. Inbound SAs are correct but no SP is found +# TYPE node_xfrm_in_no_pols_packets_total counter +node_xfrm_in_no_pols_packets_total 65432 +# HELP node_xfrm_in_no_states_packets_total No state is found i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong +# TYPE node_xfrm_in_no_states_packets_total counter +node_xfrm_in_no_states_packets_total 3 +# HELP node_xfrm_in_pol_block_packets_total Policy discards +# TYPE node_xfrm_in_pol_block_packets_total counter +node_xfrm_in_pol_block_packets_total 100 +# HELP node_xfrm_in_pol_error_packets_total Policy error +# TYPE node_xfrm_in_pol_error_packets_total counter +node_xfrm_in_pol_error_packets_total 10000 +# HELP node_xfrm_in_state_expired_packets_total State is expired +# TYPE node_xfrm_in_state_expired_packets_total counter +node_xfrm_in_state_expired_packets_total 7 +# HELP node_xfrm_in_state_invalid_packets_total State is invalid +# TYPE node_xfrm_in_state_invalid_packets_total counter +node_xfrm_in_state_invalid_packets_total 55555 +# HELP node_xfrm_in_state_mismatch_packets_total State has mismatch option e.g. UDP encapsulation type is mismatch +# TYPE node_xfrm_in_state_mismatch_packets_total counter +node_xfrm_in_state_mismatch_packets_total 23451 +# HELP node_xfrm_in_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_in_state_mode_error_packets_total counter +node_xfrm_in_state_mode_error_packets_total 100 +# HELP node_xfrm_in_state_proto_error_packets_total Transformation protocol specific error e.g. SA key is wrong +# TYPE node_xfrm_in_state_proto_error_packets_total counter +node_xfrm_in_state_proto_error_packets_total 40 +# HELP node_xfrm_in_state_seq_error_packets_total Sequence error i.e. Sequence number is out of window +# TYPE node_xfrm_in_state_seq_error_packets_total counter +node_xfrm_in_state_seq_error_packets_total 6000 +# HELP node_xfrm_in_tmpl_mismatch_packets_total No matching template for states e.g. Inbound SAs are correct but SP rule is wrong +# TYPE node_xfrm_in_tmpl_mismatch_packets_total counter +node_xfrm_in_tmpl_mismatch_packets_total 51 +# HELP node_xfrm_out_bundle_check_error_packets_total Bundle check error +# TYPE node_xfrm_out_bundle_check_error_packets_total counter +node_xfrm_out_bundle_check_error_packets_total 555 +# HELP node_xfrm_out_bundle_gen_error_packets_total Bundle generation error +# TYPE node_xfrm_out_bundle_gen_error_packets_total counter +node_xfrm_out_bundle_gen_error_packets_total 43321 +# HELP node_xfrm_out_error_packets_total All errors which is not matched others +# TYPE node_xfrm_out_error_packets_total counter +node_xfrm_out_error_packets_total 1e+06 +# HELP node_xfrm_out_no_states_packets_total No state is found +# TYPE node_xfrm_out_no_states_packets_total counter +node_xfrm_out_no_states_packets_total 869 +# HELP node_xfrm_out_pol_block_packets_total Policy discards +# TYPE node_xfrm_out_pol_block_packets_total counter +node_xfrm_out_pol_block_packets_total 43456 +# HELP node_xfrm_out_pol_dead_packets_total Policy is dead +# TYPE node_xfrm_out_pol_dead_packets_total counter +node_xfrm_out_pol_dead_packets_total 7656 +# HELP node_xfrm_out_pol_error_packets_total Policy error +# TYPE node_xfrm_out_pol_error_packets_total counter +node_xfrm_out_pol_error_packets_total 1454 +# HELP node_xfrm_out_state_expired_packets_total State is expired +# TYPE node_xfrm_out_state_expired_packets_total counter +node_xfrm_out_state_expired_packets_total 565 +# HELP node_xfrm_out_state_invalid_packets_total State is invalid, perhaps expired +# TYPE node_xfrm_out_state_invalid_packets_total counter +node_xfrm_out_state_invalid_packets_total 28765 +# HELP node_xfrm_out_state_mode_error_packets_total Transformation mode specific error +# TYPE node_xfrm_out_state_mode_error_packets_total counter +node_xfrm_out_state_mode_error_packets_total 8 +# HELP node_xfrm_out_state_proto_error_packets_total Transformation protocol specific error +# TYPE node_xfrm_out_state_proto_error_packets_total counter +node_xfrm_out_state_proto_error_packets_total 4542 +# HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow +# TYPE node_xfrm_out_state_seq_error_packets_total counter +node_xfrm_out_state_seq_error_packets_total 543 +# HELP process_open_fds Number of open file descriptors. +# TYPE process_open_fds gauge +# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. +# TYPE promhttp_metric_handler_errors_total counter +promhttp_metric_handler_errors_total{cause="encoding"} 0 +promhttp_metric_handler_errors_total{cause="gathering"} 0 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 +# HELP testmetric1_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_1 untyped +testmetric1_1{foo="bar"} 10 +# HELP testmetric1_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics1.prom +# TYPE testmetric1_2 untyped +testmetric1_2{foo="baz"} 20 +# HELP testmetric2_1 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_1 untyped +testmetric2_1{foo="bar"} 30 +# HELP testmetric2_2 Metric read from collector/fixtures/textfile/two_metric_files/metrics2.prom +# TYPE testmetric2_2 untyped +testmetric2_2{foo="baz"} 40 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 39b8c2b9..8598f447 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 @@ -506,21 +510,21 @@ node_disk_flush_requests_time_seconds_total{device="sdc"} 1.944 node_disk_flush_requests_total{device="sdc"} 1555 # HELP node_disk_info Info of /sys/block/. # TYPE node_disk_info gauge -node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",serial="",wwn=""} 1 -node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 -node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 -node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 -node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 -node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",serial="AAAABBBBCCCC1",wwn=""} 1 -node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",serial="",wwn=""} 1 +node_disk_info{device="dm-0",major="252",minor="0",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-1",major="252",minor="1",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-2",major="252",minor="2",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-3",major="252",minor="3",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-4",major="252",minor="4",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="dm-5",major="252",minor="5",model="",path="",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0",major="179",minor="0",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p1",major="179",minor="1",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="mmcblk0p2",major="179",minor="2",model="",path="platform-df2969f3.mmc",revision="",rotational="0",serial="",wwn=""} 1 +node_disk_info{device="nvme0n1",major="259",minor="0",model="SAMSUNG EHFTF55LURSY-000Y9",path="pci-0000:02:00.0-nvme-1",revision="4NBTUY95",rotational="0",serial="S252B6CU1HG3M1",wwn="eui.p3vbbiejx5aae2r3"} 1 +node_disk_info{device="sda",major="8",minor="0",model="TOSHIBA_KSDB4U86",path="pci-0000:3b:00.0-sas-phy7-lun-0",revision="0102",rotational="1",serial="2160A0D5FVGG",wwn="0x7c72382b8de36a64"} 1 +node_disk_info{device="sdb",major="8",minor="16",model="SuperMicro_SSD",path="pci-0000:00:1f.2-ata-1",revision="0R",rotational="0",serial="SMC0E1B87ABBB16BD84E",wwn="0xe1b87abbb16bd84e"} 1 +node_disk_info{device="sdc",major="8",minor="32",model="INTEL_SSDS9X9SI0",path="pci-0000:00:1f.2-ata-4",revision="0100",rotational="0",serial="3EWB5Y25CWQWA7EH1U",wwn="0x58907ddc573a5de"} 1 +node_disk_info{device="sr0",major="11",minor="0",model="Virtual_CDROM0",path="pci-0000:00:14.0-usb-0:1.1:1.0-scsi-0:0:0:0",revision="1.00",rotational="0",serial="AAAABBBBCCCC1",wwn=""} 1 +node_disk_info{device="vda",major="254",minor="0",model="",path="pci-0000:00:06.0",revision="",rotational="0",serial="",wwn=""} 1 # HELP node_disk_io_now The number of I/Os currently in progress. # TYPE node_disk_io_now gauge node_disk_io_now{device="dm-0"} 0 @@ -889,6 +893,10 @@ node_hwmon_fan_target_rpm{chip="nct6779",sensor="fan2"} 27000 # HELP node_hwmon_fan_tolerance Hardware monitor fan element tolerance # TYPE node_hwmon_fan_tolerance gauge node_hwmon_fan_tolerance{chip="nct6779",sensor="fan2"} 0 +# HELP node_hwmon_freq_freq_mhz Hardware monitor for GPU frequency in MHz +# TYPE node_hwmon_freq_freq_mhz gauge +node_hwmon_freq_freq_mhz{chip="hwmon4",sensor="mclk"} 300 +node_hwmon_freq_freq_mhz{chip="hwmon4",sensor="sclk"} 214 # HELP node_hwmon_in_alarm Hardware sensor alarm status (in) # TYPE node_hwmon_in_alarm gauge node_hwmon_in_alarm{chip="nct6779",sensor="in0"} 0 @@ -1002,8 +1010,10 @@ node_hwmon_pwm_weight_temp_step_tol{chip="nct6779",sensor="pwm1"} 0 # TYPE node_hwmon_sensor_label gauge node_hwmon_sensor_label{chip="hwmon4",label="foosensor",sensor="temp1"} 1 node_hwmon_sensor_label{chip="hwmon4",label="foosensor",sensor="temp2"} 1 -node_hwmon_sensor_label{chip="platform_applesmc_768",label="Left side ",sensor="fan1"} 1 -node_hwmon_sensor_label{chip="platform_applesmc_768",label="Right side ",sensor="fan2"} 1 +node_hwmon_sensor_label{chip="hwmon4",label="mclk",sensor="freq2"} 1 +node_hwmon_sensor_label{chip="hwmon4",label="sclk",sensor="freq1"} 1 +node_hwmon_sensor_label{chip="platform_applesmc_768",label="Left side",sensor="fan1"} 1 +node_hwmon_sensor_label{chip="platform_applesmc_768",label="Right side",sensor="fan2"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 0",sensor="temp2"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 1",sensor="temp3"} 1 node_hwmon_sensor_label{chip="platform_coretemp_0",label="Core 2",sensor="temp4"} 1 @@ -4333,108 +4343,139 @@ node_zfs_zil_zil_itx_needcopy_count 0 # TYPE node_zfs_zpool_dataset_nread untyped node_zfs_zpool_dataset_nread{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nread{dataset="pool1/dataset1",zpool="pool1"} 28 +node_zfs_zpool_dataset_nread{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nread{dataset="pool3/dataset with space",zpool="pool3"} 28 node_zfs_zpool_dataset_nread{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nread{dataset="poolz1/dataset1",zpool="poolz1"} 28 # HELP node_zfs_zpool_dataset_nunlinked kstat.zfs.misc.objset.nunlinked # TYPE node_zfs_zpool_dataset_nunlinked untyped node_zfs_zpool_dataset_nunlinked{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nunlinked{dataset="pool1/dataset1",zpool="pool1"} 3 +node_zfs_zpool_dataset_nunlinked{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nunlinked{dataset="pool3/dataset with space",zpool="pool3"} 3 node_zfs_zpool_dataset_nunlinked{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nunlinked{dataset="poolz1/dataset1",zpool="poolz1"} 14 # HELP node_zfs_zpool_dataset_nunlinks kstat.zfs.misc.objset.nunlinks # TYPE node_zfs_zpool_dataset_nunlinks untyped node_zfs_zpool_dataset_nunlinks{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nunlinks{dataset="pool1/dataset1",zpool="pool1"} 3 +node_zfs_zpool_dataset_nunlinks{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nunlinks{dataset="pool3/dataset with space",zpool="pool3"} 3 node_zfs_zpool_dataset_nunlinks{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nunlinks{dataset="poolz1/dataset1",zpool="poolz1"} 14 # HELP node_zfs_zpool_dataset_nwritten kstat.zfs.misc.objset.nwritten # TYPE node_zfs_zpool_dataset_nwritten untyped node_zfs_zpool_dataset_nwritten{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_nwritten{dataset="pool1/dataset1",zpool="pool1"} 12302 +node_zfs_zpool_dataset_nwritten{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_nwritten{dataset="pool3/dataset with space",zpool="pool3"} 12302 node_zfs_zpool_dataset_nwritten{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_nwritten{dataset="poolz1/dataset1",zpool="poolz1"} 32806 # HELP node_zfs_zpool_dataset_reads kstat.zfs.misc.objset.reads # TYPE node_zfs_zpool_dataset_reads untyped node_zfs_zpool_dataset_reads{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_reads{dataset="pool1/dataset1",zpool="pool1"} 2 +node_zfs_zpool_dataset_reads{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_reads{dataset="pool3/dataset with space",zpool="pool3"} 2 node_zfs_zpool_dataset_reads{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_reads{dataset="poolz1/dataset1",zpool="poolz1"} 2 # HELP node_zfs_zpool_dataset_writes kstat.zfs.misc.objset.writes # TYPE node_zfs_zpool_dataset_writes untyped node_zfs_zpool_dataset_writes{dataset="pool1",zpool="pool1"} 0 node_zfs_zpool_dataset_writes{dataset="pool1/dataset1",zpool="pool1"} 4 +node_zfs_zpool_dataset_writes{dataset="pool3",zpool="pool3"} 0 +node_zfs_zpool_dataset_writes{dataset="pool3/dataset with space",zpool="pool3"} 4 node_zfs_zpool_dataset_writes{dataset="poolz1",zpool="poolz1"} 0 node_zfs_zpool_dataset_writes{dataset="poolz1/dataset1",zpool="poolz1"} 10 # HELP node_zfs_zpool_nread kstat.zfs.misc.io.nread # TYPE node_zfs_zpool_nread untyped node_zfs_zpool_nread{zpool="pool1"} 1.88416e+06 +node_zfs_zpool_nread{zpool="pool3"} 1.88416e+06 node_zfs_zpool_nread{zpool="poolz1"} 2.82624e+06 # HELP node_zfs_zpool_nwritten kstat.zfs.misc.io.nwritten # TYPE node_zfs_zpool_nwritten untyped node_zfs_zpool_nwritten{zpool="pool1"} 3.206144e+06 +node_zfs_zpool_nwritten{zpool="pool3"} 3.206144e+06 node_zfs_zpool_nwritten{zpool="poolz1"} 2.680501248e+09 # HELP node_zfs_zpool_rcnt kstat.zfs.misc.io.rcnt # TYPE node_zfs_zpool_rcnt untyped node_zfs_zpool_rcnt{zpool="pool1"} 0 +node_zfs_zpool_rcnt{zpool="pool3"} 0 node_zfs_zpool_rcnt{zpool="poolz1"} 0 # HELP node_zfs_zpool_reads kstat.zfs.misc.io.reads # TYPE node_zfs_zpool_reads untyped node_zfs_zpool_reads{zpool="pool1"} 22 +node_zfs_zpool_reads{zpool="pool3"} 22 node_zfs_zpool_reads{zpool="poolz1"} 33 # HELP node_zfs_zpool_rlentime kstat.zfs.misc.io.rlentime # TYPE node_zfs_zpool_rlentime untyped node_zfs_zpool_rlentime{zpool="pool1"} 1.04112268e+08 +node_zfs_zpool_rlentime{zpool="pool3"} 1.04112268e+08 node_zfs_zpool_rlentime{zpool="poolz1"} 6.472105124093e+12 # HELP node_zfs_zpool_rtime kstat.zfs.misc.io.rtime # TYPE node_zfs_zpool_rtime untyped node_zfs_zpool_rtime{zpool="pool1"} 2.4168078e+07 +node_zfs_zpool_rtime{zpool="pool3"} 2.4168078e+07 node_zfs_zpool_rtime{zpool="poolz1"} 9.82909164e+09 # HELP node_zfs_zpool_rupdate kstat.zfs.misc.io.rupdate # TYPE node_zfs_zpool_rupdate untyped node_zfs_zpool_rupdate{zpool="pool1"} 7.921048984922e+13 +node_zfs_zpool_rupdate{zpool="pool3"} 7.921048984922e+13 node_zfs_zpool_rupdate{zpool="poolz1"} 1.10734831944501e+14 # HELP node_zfs_zpool_state kstat.zfs.misc.state # TYPE node_zfs_zpool_state gauge node_zfs_zpool_state{state="degraded",zpool="pool1"} 0 node_zfs_zpool_state{state="degraded",zpool="pool2"} 0 +node_zfs_zpool_state{state="degraded",zpool="pool3"} 0 node_zfs_zpool_state{state="degraded",zpool="poolz1"} 1 node_zfs_zpool_state{state="faulted",zpool="pool1"} 0 node_zfs_zpool_state{state="faulted",zpool="pool2"} 0 +node_zfs_zpool_state{state="faulted",zpool="pool3"} 0 node_zfs_zpool_state{state="faulted",zpool="poolz1"} 0 node_zfs_zpool_state{state="offline",zpool="pool1"} 0 node_zfs_zpool_state{state="offline",zpool="pool2"} 0 +node_zfs_zpool_state{state="offline",zpool="pool3"} 0 node_zfs_zpool_state{state="offline",zpool="poolz1"} 0 node_zfs_zpool_state{state="online",zpool="pool1"} 1 node_zfs_zpool_state{state="online",zpool="pool2"} 0 +node_zfs_zpool_state{state="online",zpool="pool3"} 1 node_zfs_zpool_state{state="online",zpool="poolz1"} 0 node_zfs_zpool_state{state="removed",zpool="pool1"} 0 node_zfs_zpool_state{state="removed",zpool="pool2"} 0 +node_zfs_zpool_state{state="removed",zpool="pool3"} 0 node_zfs_zpool_state{state="removed",zpool="poolz1"} 0 node_zfs_zpool_state{state="suspended",zpool="pool1"} 0 node_zfs_zpool_state{state="suspended",zpool="pool2"} 1 +node_zfs_zpool_state{state="suspended",zpool="pool3"} 0 node_zfs_zpool_state{state="suspended",zpool="poolz1"} 0 node_zfs_zpool_state{state="unavail",zpool="pool1"} 0 node_zfs_zpool_state{state="unavail",zpool="pool2"} 0 +node_zfs_zpool_state{state="unavail",zpool="pool3"} 0 node_zfs_zpool_state{state="unavail",zpool="poolz1"} 0 # HELP node_zfs_zpool_wcnt kstat.zfs.misc.io.wcnt # TYPE node_zfs_zpool_wcnt untyped node_zfs_zpool_wcnt{zpool="pool1"} 0 +node_zfs_zpool_wcnt{zpool="pool3"} 0 node_zfs_zpool_wcnt{zpool="poolz1"} 0 # HELP node_zfs_zpool_wlentime kstat.zfs.misc.io.wlentime # TYPE node_zfs_zpool_wlentime untyped node_zfs_zpool_wlentime{zpool="pool1"} 1.04112268e+08 +node_zfs_zpool_wlentime{zpool="pool3"} 1.04112268e+08 node_zfs_zpool_wlentime{zpool="poolz1"} 6.472105124093e+12 # HELP node_zfs_zpool_writes kstat.zfs.misc.io.writes # TYPE node_zfs_zpool_writes untyped node_zfs_zpool_writes{zpool="pool1"} 132 +node_zfs_zpool_writes{zpool="pool3"} 132 node_zfs_zpool_writes{zpool="poolz1"} 25294 # HELP node_zfs_zpool_wtime kstat.zfs.misc.io.wtime # TYPE node_zfs_zpool_wtime untyped node_zfs_zpool_wtime{zpool="pool1"} 7.155162e+06 +node_zfs_zpool_wtime{zpool="pool3"} 7.155162e+06 node_zfs_zpool_wtime{zpool="poolz1"} 9.673715628e+09 # HELP node_zfs_zpool_wupdate kstat.zfs.misc.io.wupdate # TYPE node_zfs_zpool_wupdate untyped node_zfs_zpool_wupdate{zpool="pool1"} 7.9210489694949e+13 +node_zfs_zpool_wupdate{zpool="pool3"} 7.9210489694949e+13 node_zfs_zpool_wupdate{zpool="poolz1"} 1.10734831833266e+14 # HELP node_zoneinfo_high_pages Zone watermark pages_high # TYPE node_zoneinfo_high_pages gauge @@ -4611,6 +4652,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/spl/kstat/zfs/pool3/io b/collector/fixtures/proc/spl/kstat/zfs/pool3/io new file mode 100644 index 00000000..ef2a58fe --- /dev/null +++ b/collector/fixtures/proc/spl/kstat/zfs/pool3/io @@ -0,0 +1,3 @@ +12 3 0x00 1 80 79205351707403 395818011156865 +nread nwritten reads writes wtime wlentime wupdate rtime rlentime rupdate wcnt rcnt +1884160 3206144 22 132 7155162 104112268 79210489694949 24168078 104112268 79210489849220 0 0 diff --git a/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-1 b/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-1 new file mode 100644 index 00000000..9799d262 --- /dev/null +++ b/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-1 @@ -0,0 +1,9 @@ +23 1 0x01 7 2160 221578688875 6665999035587 +name type data +dataset_name 7 pool3 +writes 4 0 +nwritten 4 0 +reads 4 0 +nread 4 0 +nunlinks 4 0 +nunlinked 4 0 diff --git a/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-2 b/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-2 new file mode 100644 index 00000000..e395504b --- /dev/null +++ b/collector/fixtures/proc/spl/kstat/zfs/pool3/objset-2 @@ -0,0 +1,9 @@ +24 1 0x01 7 2160 221611904716 7145015038451 +name type data +dataset_name 7 pool3/dataset with space +writes 4 4 +nwritten 4 12302 +reads 4 2 +nread 4 28 +nunlinks 4 3 +nunlinked 4 3 diff --git a/collector/fixtures/proc/spl/kstat/zfs/pool3/state b/collector/fixtures/proc/spl/kstat/zfs/pool3/state new file mode 100644 index 00000000..1424865c --- /dev/null +++ b/collector/fixtures/proc/spl/kstat/zfs/pool3/state @@ -0,0 +1 @@ +ONLINE diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 6fcf094d..5af46262 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -2,6 +2,243 @@ Directory: sys Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/block +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/block/sda +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/block/sda/queue +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/add_random +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/chunk_sectors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/dax +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/discard_granularity +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/discard_max_bytes +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/discard_max_hw_bytes +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/discard_zeroes_data +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/fua +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/hw_sector_size +Lines: 1 +512 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/io_poll +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/io_poll_delay +Lines: 1 +-1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/io_timeout +Lines: 1 +30000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/block/sda/queue/iosched +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/back_seek_max +Lines: 1 +16384 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/back_seek_penalty +Lines: 1 +2 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/fifo_expire_async +Lines: 1 +250 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/fifo_expire_sync +Lines: 1 +125 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/low_latency +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/max_budget +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/slice_idle +Lines: 1 +8 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/slice_idle_us +Lines: 1 +8000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/strict_guarantees +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iosched/timeout_sync +Lines: 1 +125 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/iostats +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/logical_block_size +Lines: 1 +512 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_discard_segments +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_hw_sectors_kb +Lines: 1 +32767 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_integrity_segments +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_sectors_kb +Lines: 1 +1280 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_segment_size +Lines: 1 +65536 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/max_segments +Lines: 1 +168 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/minimum_io_size +Lines: 1 +512 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/nomerges +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/nr_requests +Lines: 1 +64 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/nr_zones +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/optimal_io_size +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/physical_block_size +Lines: 1 +512 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/read_ahead_kb +Lines: 1 +128 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/rotational +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/rq_affinity +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/scheduler +Lines: 1 +mq-deadline kyber [bfq] none +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/wbt_lat_usec +Lines: 1 +75000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/write_cache +Lines: 1 +write back +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/write_same_max_bytes +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/write_zeroes_max_bytes +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/block/sda/queue/zoned +Lines: 1 +none +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/bus Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -437,6 +674,26 @@ Lines: 1 100000 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/hwmon/hwmon4/freq1_input +Lines: 1 +214000000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/hwmon/hwmon4/freq1_label +Lines: 1 +sclk +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/hwmon/hwmon4/freq2_input +Lines: 1 +300000000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/hwmon/hwmon4/freq2_label +Lines: 1 +mclk +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/class/hwmon/hwmon5 SymlinkTo: ../../devices/platform/bogus.0/hwmon/hwmon5/ # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1337,7 +1594,7 @@ Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/class/nvme/nvme0/model Lines: 1 -Samsung SSD 970 PRO 512GB +Samsung SSD 970 PRO 512GB Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/class/nvme/nvme0/serial @@ -2750,7 +3007,7 @@ Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/devices/platform/applesmc.768/fan1_label Lines: 1 -Left side +Left side Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/devices/platform/applesmc.768/fan1_manual @@ -2784,7 +3041,7 @@ Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/devices/platform/applesmc.768/fan2_label Lines: 1 -Right side +Right side Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/devices/platform/applesmc.768/fan2_manual diff --git a/collector/hwmon_linux.go b/collector/hwmon_linux.go index ed46f0f8..ad8da41b 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,8 +27,6 @@ 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" ) @@ -44,7 +44,7 @@ var ( hwmonSensorTypes = []string{ "vrm", "beep_enable", "update_interval", "in", "cpu", "fan", "pwm", "temp", "curr", "power", "energy", "humidity", - "intrusion", + "intrusion", "freq", } ) @@ -55,12 +55,12 @@ func init() { type hwMonCollector struct { deviceFilter deviceFilter sensorFilter deviceFilter - logger log.Logger + 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, @@ -108,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 } @@ -168,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 } @@ -211,7 +214,7 @@ func (c *hwMonCollector) updateHwmon(ch chan<- prometheus.Metric, dir string) er // 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) { - level.Debug(c.logger).Log("msg", "ignoring sensor", "sensor", sensor) + c.logger.Debug("ignoring sensor", "sensor", sensor) continue } @@ -354,6 +357,15 @@ func (c *hwMonCollector) updateHwmon(ch chan<- prometheus.Metric, dir string) er continue } + if sensorType == "freq" && element == "input" { + if label, ok := sensorData["label"]; ok { + sensorLabel := cleanMetricName(label) + desc := prometheus.NewDesc(name+"_freq_mhz", "Hardware monitor for GPU frequency in MHz", hwmonLabelDesc, nil) + ch <- prometheus.MustNewConstMetric( + desc, prometheus.GaugeValue, parsedValue/1000000.0, append(labels[:len(labels)-1], sensorLabel)...) + } + continue + } // fallback, just dump the metric as is desc := prometheus.NewDesc(name, "Hardware monitor "+sensorType+" element "+element, hwmonLabelDesc, nil) @@ -450,7 +462,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 b0928da3..7b1c4381 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 @@ -116,7 +115,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 a0996e91..80cd7171 100644 --- a/collector/interrupts_common.go +++ b/collector/interrupts_common.go @@ -18,14 +18,15 @@ package collector import ( + "log/slog" + "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" ) type interruptsCollector struct { desc typedDesc - logger log.Logger + logger *slog.Logger nameFilter deviceFilter includeZeros bool } @@ -41,7 +42,7 @@ var ( ) // 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", diff --git a/collector/interrupts_linux.go b/collector/interrupts_linux.go index 3d3bd037..77caf423 100644 --- a/collector/interrupts_linux.go +++ b/collector/interrupts_linux.go @@ -25,7 +25,6 @@ import ( "strconv" "strings" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -42,7 +41,7 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) (err error) { for cpuNo, value := range interrupt.values { filterName := name + ";" + interrupt.info + ";" + interrupt.devices if c.nameFilter.ignored(filterName) { - level.Debug(c.logger).Log("msg", "ignoring interrupt name", "filter_name", filterName) + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) continue } fv, err := strconv.ParseFloat(value, 64) @@ -50,7 +49,7 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) (err error) { return fmt.Errorf("invalid value %s in interrupts: %w", value, err) } if !c.includeZeros && fv == 0.0 { - level.Debug(c.logger).Log("msg", "ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + 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 049f6407..60fcbf1d 100644 --- a/collector/interrupts_openbsd.go +++ b/collector/interrupts_openbsd.go @@ -20,7 +20,6 @@ import ( "fmt" "strconv" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -110,11 +109,11 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) error { interruptType := fmt.Sprintf("%d", interrupt.vector) filterName := interruptType + ";" + dev if c.nameFilter.ignored(filterName) { - level.Debug(c.logger).Log("msg", "ignoring interrupt name", "filter_name", filterName) + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) continue } if !c.includeZeros && value == 0.0 { - level.Debug(c.logger).Log("msg", "ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + c.logger.Debug("ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) continue } ch <- c.desc.mustNewConstMetric( diff --git a/collector/interrupts_openbsd_amd64.go b/collector/interrupts_openbsd_amd64.go index 4ce4edfd..69851a22 100644 --- a/collector/interrupts_openbsd_amd64.go +++ b/collector/interrupts_openbsd_amd64.go @@ -21,7 +21,6 @@ import ( "strconv" "unsafe" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -81,11 +80,11 @@ func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) error { interruptType := fmt.Sprintf("%d", interrupt.vector) filterName := interruptType + ";" + dev if c.nameFilter.ignored(filterName) { - level.Debug(c.logger).Log("msg", "ignoring interrupt name", "filter_name", filterName) + c.logger.Debug("ignoring interrupt name", "filter_name", filterName) continue } if !c.includeZeros && value == 0.0 { - level.Debug(c.logger).Log("msg", "ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) + c.logger.Debug("ignoring interrupt with zero value", "filter_name", filterName, "cpu", cpuNo) continue } ch <- c.desc.mustNewConstMetric( 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 0a6390d8..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,7 +21,6 @@ import ( "fmt" "strings" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" ) @@ -41,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", fmt.Sprintf("%v", 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 21316fb5..b5b10b4b 100644 --- a/collector/meminfo_darwin.go +++ b/collector/meminfo_darwin.go @@ -24,18 +24,18 @@ import "C" import ( "encoding/binary" "fmt" + "log/slog" "unsafe" - "github.com/go-kit/log" "golang.org/x/sys/unix" ) type meminfoCollector struct { - logger log.Logger + logger *slog.Logger } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { return &meminfoCollector{ logger: logger, }, nil diff --git a/collector/meminfo_linux.go b/collector/meminfo_linux.go index 40e06990..98d5a5c0 100644 --- a/collector/meminfo_linux.go +++ b/collector/meminfo_linux.go @@ -18,18 +18,18 @@ package collector import ( "fmt" + "log/slog" - "github.com/go-kit/log" "github.com/prometheus/procfs" ) type meminfoCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { +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) diff --git a/collector/meminfo_linux_test.go b/collector/meminfo_linux_test.go index 523426d0..41a0133f 100644 --- a/collector/meminfo_linux_test.go +++ b/collector/meminfo_linux_test.go @@ -17,15 +17,14 @@ package collector import ( - "os" + "io" + "log/slog" "testing" - - "github.com/go-kit/log" ) func TestMemInfo(t *testing.T) { *procPath = "fixtures/proc" - logger := log.NewLogfmtLogger(os.Stderr) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) collector, err := NewMeminfoCollector(logger) if err != nil { diff --git a/collector/meminfo_netbsd.go b/collector/meminfo_netbsd.go index f8b131e5..54d23a12 100644 --- a/collector/meminfo_netbsd.go +++ b/collector/meminfo_netbsd.go @@ -17,16 +17,16 @@ package collector import ( - "github.com/go-kit/log" "golang.org/x/sys/unix" + "log/slog" ) type meminfoCollector struct { - logger log.Logger + logger *slog.Logger } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { return &meminfoCollector{ logger: logger, }, 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 4a4682a3..ca9f46ad 100644 --- a/collector/meminfo_openbsd.go +++ b/collector/meminfo_openbsd.go @@ -18,8 +18,7 @@ package collector import ( "fmt" - - "github.com/go-kit/log" + "log/slog" ) /* @@ -56,11 +55,11 @@ sysctl_bcstats(struct bcachestats *bcstats) import "C" type meminfoCollector struct { - logger log.Logger + logger *slog.Logger } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { return &meminfoCollector{ logger: logger, }, nil diff --git a/collector/meminfo_openbsd_amd64.go b/collector/meminfo_openbsd_amd64.go index 2f81c79e..906a36e4 100644 --- a/collector/meminfo_openbsd_amd64.go +++ b/collector/meminfo_openbsd_amd64.go @@ -17,9 +17,9 @@ package collector import ( + "log/slog" "unsafe" - "github.com/go-kit/log" "golang.org/x/sys/unix" ) @@ -51,11 +51,11 @@ type bcachestats struct { } type meminfoCollector struct { - logger log.Logger + logger *slog.Logger } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector(logger log.Logger) (Collector, error) { +func NewMeminfoCollector(logger *slog.Logger) (Collector, error) { return &meminfoCollector{ logger: logger, }, 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 cdd84ea1..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) @@ -542,7 +541,7 @@ func (c *mountStatsCollector) Update(ch chan<- prometheus.Metric) error { deviceIdentifier := nfsDeviceIdentifier{m.Device, stats.Transport[k].Protocol, mountAddress} i := deviceList[deviceIdentifier] if i { - level.Debug(c.logger).Log("msg", "Skipping duplicate device entry", "device", deviceIdentifier) + c.logger.Debug("Skipping duplicate device entry", "device", deviceIdentifier) break } deviceList[deviceIdentifier] = true diff --git a/collector/netclass_linux.go b/collector/netclass_linux.go index 327c89aa..87763c3b 100644 --- a/collector/netclass_linux.go +++ b/collector/netclass_linux.go @@ -19,14 +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" ) @@ -43,7 +42,7 @@ type netClassCollector struct { ignoredDevicesPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc metricDescsMu sync.Mutex - logger log.Logger + logger *slog.Logger } func init() { @@ -51,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) @@ -77,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) 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_freebsd.go b/collector/netstat_freebsd.go new file mode 100644 index 00000000..3c34d8b3 --- /dev/null +++ b/collector/netstat_freebsd.go @@ -0,0 +1,108 @@ +// 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 freebsd +// +build freebsd + +package collector + +import ( + "errors" + "fmt" + "log/slog" + "unsafe" + + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" +) + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +var ( + bsdNetstatTcpSendPacketsTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "netstat", "tcp_transmit_packets_total"), + "TCP packets sent", + nil, nil, + ) + + bsdNetstatTcpRecvPacketsTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "netstat", "tcp_receive_packets_total"), + "TCP packets received", + nil, nil, + ) +) + +type netStatCollector struct { + netStatMetric *prometheus.Desc +} + +func init() { + registerCollector("netstat", defaultEnabled, NewNetStatCollector) +} + +func NewNetStatCollector(logger *slog.Logger) (Collector, error) { + return &netStatCollector{}, nil +} + +func (c *netStatCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.netStatMetric +} + +func (c *netStatCollector) Collect(ch chan<- prometheus.Metric) { + _ = c.Update(ch) +} + +func getData(queryString string) ([]byte, error) { + data, err := unix.SysctlRaw(queryString) + if err != nil { + fmt.Println("Error:", err) + return nil, err + } + + if len(data) < int(unsafe.Sizeof(C.struct_tcpstat{})) { + return nil, errors.New("Data Size mismatch") + } + return data, nil +} + +func (c *netStatCollector) Update(ch chan<- prometheus.Metric) error { + + tcpData, err := getData("net.inet.tcp.stats") + if err != nil { + return err + } + + tcpStats := *(*C.struct_tcpstat)(unsafe.Pointer(&tcpData[0])) + + ch <- prometheus.MustNewConstMetric( + bsdNetstatTcpSendPacketsTotal, + prometheus.CounterValue, + float64(tcpStats.tcps_sndtotal), + ) + + ch <- prometheus.MustNewConstMetric( + bsdNetstatTcpRecvPacketsTotal, + prometheus.CounterValue, + float64(tcpStats.tcps_rcvtotal), + ) + + return nil +} diff --git a/collector/netstat_freebsd_test.go b/collector/netstat_freebsd_test.go new file mode 100644 index 00000000..3b3f8523 --- /dev/null +++ b/collector/netstat_freebsd_test.go @@ -0,0 +1,77 @@ +// 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 freebsd +// +build freebsd + +package collector + +import ( + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" + "testing" + "unsafe" +) + +func TestNetStatCollectorDescribe(t *testing.T) { + ch := make(chan *prometheus.Desc, 1) + collector := &netStatCollector{ + netStatMetric: prometheus.NewDesc("dummy_metric", "dummy", nil, nil), + } + collector.Describe(ch) + desc := <-ch + + if want, got := "dummy_metric", desc.String(); want != got { + t.Errorf("want %s, got %s", want, got) + } +} + +func TestGetData(t *testing.T) { + data, err := getData("net.inet.tcp.stats") + if err != nil { + t.Fatal("unexpected error:", err) + } + + if got, want := len(data), int(unsafe.Sizeof(unix.TCPStats{})); got < want { + t.Errorf("data length too small: want >= %d, got %d", want, got) + } +} + +func TestNetStatCollectorUpdate(t *testing.T) { + ch := make(chan prometheus.Metric, len(metrics)) + collector := &netStatCollector{ + netStatMetric: prometheus.NewDesc("netstat_metric", "NetStat Metric", nil, nil), + } + err := collector.Update(ch) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if got, want := len(ch), len(metrics); got != want { + t.Errorf("metric count mismatch: want %d, got %d", want, got) + } + + for range metrics { + <-ch + } +} + +func TestNewNetStatCollector(t *testing.T) { + collector, err := NewNetStatCollector(nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + if collector == nil { + t.Fatal("collector is nil, want non-nil") + } +} diff --git a/collector/netstat_linux.go b/collector/netstat_linux.go index afbf8d58..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" ) @@ -41,7 +41,7 @@ var ( 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 4c86740e..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" ) @@ -58,7 +60,7 @@ type osRelease struct { type osReleaseCollector struct { infoDesc *prometheus.Desc - logger log.Logger + logger *slog.Logger os *osRelease osMutex sync.RWMutex osReleaseFilenames []string // all os-release file names to check @@ -82,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( @@ -178,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 diff --git a/collector/os_release_test.go b/collector/os_release_test.go index b45ee0ed..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)" @@ -129,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 36e06505..7338b7e6 100644 --- a/collector/pressure_linux.go +++ b/collector/pressure_linux.go @@ -19,11 +19,10 @@ 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" ) @@ -42,7 +41,7 @@ type pressureStatsCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger } func init() { @@ -50,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) @@ -95,15 +94,15 @@ 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) @@ -111,11 +110,11 @@ func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { // 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" { - level.Debug(c.logger).Log("msg", "pressure information returned no 'some' data") + c.logger.Debug("pressure information returned no 'some' data") return ErrNoData } if vals.Full == nil && res != "cpu" { - level.Debug(c.logger).Log("msg", "pressure information returned no 'full' data") + c.logger.Debug("pressure information returned no 'full' data") return ErrNoData } switch res { @@ -130,7 +129,7 @@ func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { 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..2813bfd0 100644 --- a/collector/runit.go +++ b/collector/runit.go @@ -17,9 +17,9 @@ package collector import ( + "log/slog" + "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" ) @@ -31,7 +31,7 @@ type runitCollector struct { stateDesired typedDesc stateNormal typedDesc stateTimestamp typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -39,14 +39,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 +82,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..f10b43fa 100644 --- a/collector/selinux_linux.go +++ b/collector/selinux_linux.go @@ -17,7 +17,8 @@ package collector import ( - "github.com/go-kit/log" + "log/slog" + "github.com/opencontainers/selinux/go-selinux" "github.com/prometheus/client_golang/prometheus" ) @@ -26,7 +27,7 @@ type selinuxCollector struct { configMode *prometheus.Desc currentMode *prometheus.Desc enabled *prometheus.Desc - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +35,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 70241a08..aa871548 100644 --- a/collector/slabinfo_linux.go +++ b/collector/slabinfo_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" ) @@ -32,7 +32,7 @@ var ( type slabinfoCollector struct { fs procfs.FS - logger log.Logger + logger *slog.Logger subsystem string labels []string slabNameFilter deviceFilter @@ -42,7 +42,7 @@ 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) 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..73a9f7be 100644 --- a/collector/softirqs_common.go +++ b/collector/softirqs_common.go @@ -18,7 +18,8 @@ package collector import ( "fmt" - "github.com/go-kit/log" + "log/slog" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -26,7 +27,7 @@ import ( type softirqsCollector struct { fs procfs.FS desc typedDesc - logger log.Logger + logger *slog.Logger } func init() { @@ -34,7 +35,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/softirq_linux.go b/collector/softirqs_linux.go similarity index 100% rename from collector/softirq_linux.go rename to collector/softirqs_linux.go 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 e0d35bcd..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" ) @@ -78,7 +77,7 @@ type systemdCollector struct { // 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 4f1fd0b8..3e0cc459 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() @@ -195,11 +194,15 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { 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) @@ -207,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 { @@ -224,7 +227,7 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { if mf.Help != nil && helpTexts[0] != *mf.Help || helpTexts[1] != "" { metricsNamesToHelpTexts[*mf.Name] = [2]string{helpTexts[0], *mf.Help} errored = true - level.Error(c.logger).Log("msg", "inconsistent metric help text", + c.logger.Error("inconsistent metric help text", "metric", *mf.Name, "original_help_text", helpTexts[0], "new_help_text", *mf.Help, @@ -242,7 +245,7 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { 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 } @@ -250,10 +253,16 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { } } + mfHelp := make(map[string]*string) for _, mf := range parsedFamilies { if mf.Help == nil { + if help, ok := mfHelp[*mf.Name]; ok { + mf.Help = help + continue + } help := fmt.Sprintf("Metric read from %s", strings.Join(metricsNamesToFiles[*mf.Name], ", ")) mf.Help = &help + mfHelp[*mf.Name] = &help } } 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..32ca08c4 100644 --- a/collector/uname.go +++ b/collector/uname.go @@ -11,14 +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" + "log/slog" + "github.com/prometheus/client_golang/prometheus" ) @@ -37,7 +38,7 @@ var unameDesc = prometheus.NewDesc( ) type unameCollector struct { - logger log.Logger + logger *slog.Logger } type uname struct { SysName string @@ -49,11 +50,11 @@ type uname struct { } func init() { - registerCollector("uname", defaultEnabled, newUnameCollector) + registerCollector("uname", defaultEnabled, NewUnameCollector) } // 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/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 0abb1332..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 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, - ) -} 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 804d4f58..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, "mib", m.mib, "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 e1bf2c97..4baf2b35 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 @@ -85,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 } @@ -107,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 } @@ -126,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 } @@ -134,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 } @@ -240,7 +301,8 @@ func (c *zfsCollector) parsePoolObjsetFile(reader io.Reader, zpoolPath string, h parseLine := false var zpoolName, datasetName string for scanner.Scan() { - parts := strings.Fields(scanner.Text()) + line := scanner.Text() + parts := strings.Fields(line) if !parseLine && len(parts) == 3 && parts[0] == "name" && parts[1] == "type" && parts[2] == "data" { parseLine = true @@ -254,7 +316,7 @@ func (c *zfsCollector) parsePoolObjsetFile(reader io.Reader, zpoolPath string, h zpoolPathElements := strings.Split(zpoolPath, "/") pathLen := len(zpoolPathElements) zpoolName = zpoolPathElements[pathLen-2] - datasetName = parts[2] + datasetName = line[strings.Index(line, parts[2]):] continue } @@ -305,3 +367,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 3b653f4c..7b019e8c 100644 --- a/collector/zfs_linux_test.go +++ b/collector/zfs_linux_test.go @@ -38,11 +38,11 @@ func TestArcstatsParsing(t *testing.T) { err = c.parseProcfsFile(arcstatsFile, "arcstats", func(s zfsSysctl, v interface{}) { if s == zfsSysctl("kstat.zfs.misc.arcstats.hits") { - if v.(uint64) != uint64(8772612) { + 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) != int64(-922337203685477580) { + if v.(int64) != -922337203685477580 { t.Fatalf("Incorrect value parsed from procfs data") } } else { @@ -80,7 +80,7 @@ func TestZfetchstatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(7067992) { + if v.(uint64) != 7067992 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -116,7 +116,7 @@ func TestZilParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(10) { + if v.(uint64) != 10 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -152,7 +152,7 @@ func TestVdevCacheStatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(40) { + if v.(uint64) != 40 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -188,7 +188,7 @@ func TestXuioStatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(32) { + if v.(uint64) != 32 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -224,7 +224,7 @@ func TestFmParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(18) { + if v.(uint64) != 18 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -260,7 +260,7 @@ func TestDmuTxParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(3532844) { + if v.(uint64) != 3532844 { t.Fatalf("Incorrect value parsed from procfs data") } @@ -300,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) } @@ -315,6 +315,55 @@ func TestZpoolParsing(t *testing.T) { } } +func TestZpoolObjsetParsingWithSpace(t *testing.T) { + tests := []struct { + path string + expectedDataset string + }{ + { + path: "fixtures/proc/spl/kstat/zfs/pool1/objset-1", + expectedDataset: "pool1", + }, + { + path: "fixtures/proc/spl/kstat/zfs/pool1/objset-2", + expectedDataset: "pool1/dataset1", + }, + { + path: "fixtures/proc/spl/kstat/zfs/pool3/objset-1", + expectedDataset: "pool3", + }, + { + path: "fixtures/proc/spl/kstat/zfs/pool3/objset-2", + expectedDataset: "pool3/dataset with space", + }, + } + + c := zfsCollector{} + + var handlerCalled bool + for _, test := range tests { + file, err := os.Open(test.path) + if err != nil { + t.Fatal(err) + } + + handlerCalled = false + err = c.parsePoolObjsetFile(file, test.path, func(poolName string, datasetName string, s zfsSysctl, v uint64) { + handlerCalled = true + if test.expectedDataset != datasetName { + t.Fatalf("Incorrectly parsed dataset name: expected: '%s', got: '%s'", test.expectedDataset, datasetName) + } + }) + file.Close() + if err != nil { + t.Fatal(err) + } + if !handlerCalled { + t.Fatalf("Zpool parsing handler was not called for '%s'", test.path) + } + } +} + func TestZpoolObjsetParsing(t *testing.T) { zpoolPaths, err := filepath.Glob("fixtures/proc/spl/kstat/zfs/*/objset-*") if err != nil { @@ -340,7 +389,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) } @@ -376,7 +425,7 @@ func TestAbdstatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(223232) { + if v.(uint64) != 223232 { t.Fatalf("Incorrect value parsed from procfs abdstats data") } @@ -412,7 +461,7 @@ func TestDbufstatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(108807) { + if v.(uint64) != 108807 { t.Fatalf("Incorrect value parsed from procfs dbufstats data") } @@ -448,7 +497,7 @@ func TestDnodestatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(37617) { + if v.(uint64) != 37617 { t.Fatalf("Incorrect value parsed from procfs dnodestats data") } @@ -484,7 +533,7 @@ func TestVdevMirrorstatsParsing(t *testing.T) { handlerCalled = true - if v.(uint64) != uint64(94) { + if v.(uint64) != 94 { t.Fatalf("Incorrect value parsed from procfs vdev_mirror_stats data") } @@ -521,26 +570,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/alerts/alerts.libsonnet b/docs/node-mixin/alerts/alerts.libsonnet index c3464809..61d9dd2d 100644 --- a/docs/node-mixin/alerts/alerts.libsonnet +++ b/docs/node-mixin/alerts/alerts.libsonnet @@ -10,7 +10,7 @@ ( node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} / node_filesystem_size_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} * 100 < %(fsSpaceFillingUpWarningThreshold)d and - predict_linear(node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[%(fsSpaceFillingUpPredictionWindow)s], 24*60*60) < 0 + predict_linear(node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[%(fsSpaceFillingUpPredictionWindow)s], %(nodeWarningWindowHours)s*60*60) < 0 and node_filesystem_readonly{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} == 0 ) @@ -20,7 +20,7 @@ severity: 'warning', }, annotations: { - summary: 'Filesystem is predicted to run out of space within the next 24 hours.', + summary: 'Filesystem is predicted to run out of space within the next %(nodeWarningWindowHours)s hours.' % $._config, description: 'Filesystem on {{ $labels.device }}, mounted on {{ $labels.mountpoint }}, at {{ $labels.instance }} has only {{ printf "%.2f" $value }}% available space left and is filling up.', }, }, @@ -30,7 +30,7 @@ ( node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} / node_filesystem_size_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} * 100 < %(fsSpaceFillingUpCriticalThreshold)d and - predict_linear(node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], 4*60*60) < 0 + predict_linear(node_filesystem_avail_bytes{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], %(nodeCriticalWindowHours)s*60*60) < 0 and node_filesystem_readonly{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} == 0 ) @@ -40,7 +40,7 @@ severity: '%(nodeCriticalSeverity)s' % $._config, }, annotations: { - summary: 'Filesystem is predicted to run out of space within the next 4 hours.', + summary: 'Filesystem is predicted to run out of space within the next %(nodeCriticalWindowHours)s hours.' % $._config, description: 'Filesystem on {{ $labels.device }}, mounted on {{ $labels.mountpoint }}, at {{ $labels.instance }} has only {{ printf "%.2f" $value }}% available space left and is filling up fast.', }, }, @@ -86,7 +86,7 @@ ( node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} / node_filesystem_files{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} * 100 < 40 and - predict_linear(node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], 24*60*60) < 0 + predict_linear(node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], %(nodeWarningWindowHours)s*60*60) < 0 and node_filesystem_readonly{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} == 0 ) @@ -96,7 +96,7 @@ severity: 'warning', }, annotations: { - summary: 'Filesystem is predicted to run out of inodes within the next 24 hours.', + summary: 'Filesystem is predicted to run out of inodes within the next %(nodeWarningWindowHours)s hours.' % $._config, description: 'Filesystem on {{ $labels.device }}, mounted on {{ $labels.mountpoint }}, at {{ $labels.instance }} has only {{ printf "%.2f" $value }}% available inodes left and is filling up.', }, }, @@ -106,7 +106,7 @@ ( node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} / node_filesystem_files{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} * 100 < 20 and - predict_linear(node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], 4*60*60) < 0 + predict_linear(node_filesystem_files_free{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s}[6h], %(nodeCriticalWindowHours)s*60*60) < 0 and node_filesystem_readonly{%(nodeExporterSelector)s,%(fsSelector)s,%(fsMountpointSelector)s} == 0 ) @@ -116,7 +116,7 @@ severity: '%(nodeCriticalSeverity)s' % $._config, }, annotations: { - summary: 'Filesystem is predicted to run out of inodes within the next 4 hours.', + summary: 'Filesystem is predicted to run out of inodes within the next %(nodeCriticalWindowHours)s hours.' % $._config, description: 'Filesystem on {{ $labels.device }}, mounted on {{ $labels.mountpoint }}, at {{ $labels.instance }} has only {{ printf "%.2f" $value }}% available inodes left and is filling up fast.', }, }, @@ -191,7 +191,7 @@ ||| % $._config, annotations: { summary: 'Number of conntrack are getting close to the limit.', - description: '{{ $value | humanizePercentage }} of conntrack entries are used.', + description: '{{ $labels.instance }} {{ $value | humanizePercentage }} of conntrack entries are used.', }, labels: { severity: 'warning', @@ -312,7 +312,7 @@ { alert: 'NodeCPUHighUsage', expr: ||| - sum without(mode) (avg without (cpu) (rate(node_cpu_seconds_total{%(nodeExporterSelector)s, mode!="idle"}[2m]))) * 100 > %(cpuHighUsageThreshold)d + sum without(mode) (avg without (cpu) (rate(node_cpu_seconds_total{%(nodeExporterSelector)s, mode!~"idle|iowait"}[2m]))) * 100 > %(cpuHighUsageThreshold)d ||| % $._config, 'for': '15m', labels: { @@ -407,6 +407,20 @@ description: 'Systemd service {{ $labels.name }} has entered failed state at {{ $labels.instance }}', }, }, + { + alert: 'NodeSystemdServiceCrashlooping', + expr: ||| + increase(node_systemd_service_restart_total{%(nodeExporterSelector)s}[5m]) > 2 + ||| % $._config, + 'for': '15m', + labels: { + severity: 'warning', + }, + annotations: { + summary: 'Systemd service keeps restaring, possibly crash looping.', + description: 'Systemd service {{ $labels.name }} has being restarted too many times at {{ $labels.instance }} for the last 15 minutes. Please check if service is crash looping.', + }, + }, { alert: 'NodeBondingDegraded', expr: ||| diff --git a/docs/node-mixin/config.libsonnet b/docs/node-mixin/config.libsonnet index df2af23f..1a4b3caa 100644 --- a/docs/node-mixin/config.libsonnet +++ b/docs/node-mixin/config.libsonnet @@ -50,6 +50,16 @@ // 'NodeSystemSaturation' alert. systemSaturationPerCoreThreshold: 2, + // Some of the alerts use predict_linear() to fire alerts ahead of time to + // prevent unrecoverable situations (eg. no more disk space). However, the + // node may have automatic processes (cronjobs) in place to prevent that + // within a certain time window, this may not align with the default time + // window of these alerts. This can cause these alerts to start flapping. + // By reducing the time window, the system gets more time to + // resolve this before problems occur. + nodeWarningWindowHours: '24', + nodeCriticalWindowHours: '4', + // Available disk space (%) thresholds on which to trigger the // 'NodeFilesystemSpaceFillingUp' alerts. These alerts fire if the disk // usage grows in a way that it is predicted to run out in 4h or 1d diff --git a/docs/node-mixin/dashboards/node.libsonnet b/docs/node-mixin/dashboards/node.libsonnet index a72f3ee7..41e29033 100644 --- a/docs/node-mixin/dashboards/node.libsonnet +++ b/docs/node-mixin/dashboards/node.libsonnet @@ -3,5 +3,6 @@ grafanaDashboards+:: { '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 fce411ab..f9d9e07c 100644 --- a/docs/node-mixin/dashboards/use.libsonnet +++ b/docs/node-mixin/dashboards/use.libsonnet @@ -1,469 +1,480 @@ -local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet'; +local grafana = import 'github.com/grafana/grafonnet/gen/grafonnet-latest/main.libsonnet'; local dashboard = grafana.dashboard; -local row = grafana.row; -local prometheus = grafana.prometheus; -local template = grafana.template; -local graphPanel = grafana.graphPanel; +local variable = dashboard.variable; +local row = grafana.panel.row; +local prometheus = grafana.query.prometheus; + +local timeSeriesPanel = grafana.panel.timeSeries; +local tsOptions = timeSeriesPanel.options; +local tsStandardOptions = timeSeriesPanel.standardOptions; +local tsQueryOptions = timeSeriesPanel.queryOptions; +local tsCustom = timeSeriesPanel.fieldConfig.defaults.custom; +local tsLegend = tsOptions.legend; local c = import '../config.libsonnet'; -local datasourceTemplate = { - current: { - text: 'default', - value: 'default', - }, - hide: 0, - label: 'Data Source', - name: 'datasource', - options: [], - query: 'prometheus', - refresh: 1, - regex: '', - type: 'datasource', -}; +local datasource = variable.datasource.new( + 'datasource', 'prometheus' +); + +local tsCommonPanelOptions = + variable.query.withDatasourceFromVariable(datasource) + + tsCustom.stacking.withMode('normal') + + tsCustom.withFillOpacity(100) + + tsCustom.withShowPoints('never') + + tsLegend.withShowLegend(false) + + tsOptions.tooltip.withMode('multi') + + tsOptions.tooltip.withSort('desc'); local CPUUtilisation = - graphPanel.new( + timeSeriesPanel.new( 'CPU Utilisation', - datasource='$datasource', - span=6, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); local CPUSaturation = // TODO: Is this a useful panel? At least there should be some explanation how load // average relates to the "CPU saturation" in the title. - graphPanel.new( + timeSeriesPanel.new( 'CPU Saturation (Load1 per CPU)', - datasource='$datasource', - span=6, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); local memoryUtilisation = - graphPanel.new( + timeSeriesPanel.new( 'Memory Utilisation', - datasource='$datasource', - span=6, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); local memorySaturation = - graphPanel.new( + timeSeriesPanel.new( 'Memory Saturation (Major Page Faults)', - datasource='$datasource', - span=6, - format='rds', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('rds'); + +local networkOverrides = tsStandardOptions.withOverrides( + [ + tsStandardOptions.override.byRegexp.new('/Transmit/') + + tsStandardOptions.override.byRegexp.withPropertiesFromOptions( + tsCustom.withTransform('negative-Y') + ), + ] +); local networkUtilisation = - graphPanel.new( + timeSeriesPanel.new( 'Network Utilisation (Bytes Receive/Transmit)', - datasource='$datasource', - span=6, - format='Bps', - stack=true, - fill=10, - legend_show=false, ) - .addSeriesOverride({ alias: '/Receive/', stack: 'A' }) - .addSeriesOverride({ alias: '/Transmit/', stack: 'B', transform: 'negative-Y' }) - { tooltip+: { sort: 2 } }; + + tsCommonPanelOptions + + tsStandardOptions.withUnit('Bps') + + networkOverrides; local networkSaturation = - graphPanel.new( + timeSeriesPanel.new( 'Network Saturation (Drops Receive/Transmit)', - datasource='$datasource', - span=6, - format='Bps', - stack=true, - fill=10, - legend_show=false, ) - .addSeriesOverride({ alias: '/ Receive/', stack: 'A' }) - .addSeriesOverride({ alias: '/ Transmit/', stack: 'B', transform: 'negative-Y' }) - { tooltip+: { sort: 2 } }; + + tsCommonPanelOptions + + tsStandardOptions.withUnit('Bps') + + networkOverrides; local diskIOUtilisation = - graphPanel.new( + timeSeriesPanel.new( 'Disk IO Utilisation', - datasource='$datasource', - span=6, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); local diskIOSaturation = - graphPanel.new( + timeSeriesPanel.new( 'Disk IO Saturation', - datasource='$datasource', - span=6, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); local diskSpaceUtilisation = - graphPanel.new( + timeSeriesPanel.new( 'Disk Space Utilisation', - datasource='$datasource', - span=12, - format='percentunit', - stack=true, - fill=10, - legend_show=false, - ) { tooltip+: { sort: 2 } }; + ) + + tsCommonPanelOptions + + tsStandardOptions.withUnit('percentunit'); { - _clusterTemplate:: template.new( - name='cluster', - datasource='$datasource', - query='label_values(node_time_seconds, %s)' % $._config.clusterLabel, - current='', - hide=if $._config.showMultiCluster then '' else '2', - refresh=2, - includeAll=false, - sort=1 - ), + _clusterVariable:: + variable.query.new('cluster') + + variable.query.withDatasourceFromVariable(datasource) + + variable.query.queryTypes.withLabelValues( + $._config.clusterLabel, + 'node_time_seconds', + ) + + (if $._config.showMultiCluster then variable.query.generalOptions.showOnDashboard.withLabelAndValue() else variable.query.generalOptions.showOnDashboard.withNothing()) + + variable.query.refresh.onTime() + + variable.query.selectionOptions.withIncludeAll(false) + + variable.query.withSort(asc=true), grafanaDashboards+:: { 'node-rsrc-use.json': - dashboard.new( '%sUSE Method / Node' % $._config.dashboardNamePrefix, - time_from='now-1h', - tags=($._config.dashboardTags), - timezone='utc', - refresh='30s', - graphTooltip='shared_crosshair', - uid=std.md5('node-rsrc-use.json') ) - .addTemplate(datasourceTemplate) - .addTemplate($._clusterTemplate) - .addTemplate( - template.new( + + dashboard.time.withFrom('now-1h') + + dashboard.withTags($._config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withUid(std.md5('node-rsrc-use.json')) + + dashboard.withVariables([ + datasource, + $._clusterVariable, + variable.query.new('instance') + + variable.query.withDatasourceFromVariable(datasource) + + variable.query.queryTypes.withLabelValues( 'instance', - '$datasource', - 'label_values(node_exporter_build_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"}, instance)' % $._config, - refresh='time', - sort=1 - ) - ) - .addRow( - row.new('CPU') - .addPanel(CPUUtilisation.addTarget(prometheus.target('instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Utilisation'))) - .addPanel(CPUSaturation.addTarget(prometheus.target('instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Saturation'))) - ) - .addRow( - row.new('Memory') - .addPanel(memoryUtilisation.addTarget(prometheus.target('instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Utilisation'))) - .addPanel(memorySaturation.addTarget(prometheus.target('instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Major page Faults'))) - ) - .addRow( - row.new('Network') - .addPanel( - networkUtilisation - .addTarget(prometheus.target('instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Receive')) - .addTarget(prometheus.target('instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Transmit')) - ) - .addPanel( - networkSaturation - .addTarget(prometheus.target('instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Receive')) - .addTarget(prometheus.target('instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='Transmit')) - ) - ) - .addRow( - row.new('Disk IO') - .addPanel(diskIOUtilisation.addTarget(prometheus.target('instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{device}}'))) - .addPanel(diskIOSaturation.addTarget(prometheus.target('instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{device}}'))) - ) - .addRow( - row.new('Disk Space') - .addPanel( - diskSpaceUtilisation.addTarget(prometheus.target( - ||| - sort_desc(1 - - ( - max without (mountpoint, fstype) (node_filesystem_avail_bytes{%(nodeExporterSelector)s, fstype!="", instance="$instance", %(clusterLabel)s="$cluster"}) - / - max without (mountpoint, fstype) (node_filesystem_size_bytes{%(nodeExporterSelector)s, fstype!="", instance="$instance", %(clusterLabel)s="$cluster"}) - ) != 0 - ) - ||| % $._config, legendFormat='{{device}}' - )) + 'node_exporter_build_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"}' % $._config, ) + + variable.query.refresh.onTime() + + variable.query.withSort(asc=true), + ]) + + dashboard.withPanels( + grafana.util.grid.makeGrid([ + row.new('CPU') + + row.withPanels([ + CPUUtilisation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Utilisation')]), + CPUSaturation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Saturation')]), + ]), + row.new('Memory') + + row.withPanels([ + memoryUtilisation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Utilisation')]), + memorySaturation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Major page Faults')]), + ]), + row.new('Network') + + row.withPanels([ + networkUtilisation + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Receive'), + prometheus.new('$datasource', 'instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Transmit'), + ]), + networkSaturation + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Receive'), + prometheus.new('$datasource', 'instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('Transmit'), + ]), + ]), + row.new('Disk IO') + + row.withPanels([ + diskIOUtilisation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('{{device}}')]), + diskIOSaturation + tsQueryOptions.withTargets([prometheus.new('$datasource', 'instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} != 0' % $._config) + prometheus.withLegendFormat('{{device}}')]), + ]), + ], panelWidth=12, panelHeight=7) + + grafana.util.grid.makeGrid([ + row.new('Disk Space') + + row.withPanels([ + diskSpaceUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sort_desc(1 - + ( + max without (mountpoint, fstype) (node_filesystem_avail_bytes{%(nodeExporterSelector)s, fstype!="", instance="$instance", %(clusterLabel)s="$cluster"}) + / + max without (mountpoint, fstype) (node_filesystem_size_bytes{%(nodeExporterSelector)s, fstype!="", instance="$instance", %(clusterLabel)s="$cluster"}) + ) != 0 + ) + ||| % $._config + ) + prometheus.withLegendFormat('{{device}}'), + ]), + ]), + ], panelWidth=24, panelHeight=7, startY=34), ), - 'node-cluster-rsrc-use.json': dashboard.new( '%sUSE Method / Cluster' % $._config.dashboardNamePrefix, - time_from='now-1h', - tags=($._config.dashboardTags), - timezone='utc', - refresh='30s', - graphTooltip='shared_crosshair', - uid=std.md5('node-cluster-rsrc-use.json') ) - .addTemplate(datasourceTemplate) - .addTemplate($._clusterTemplate) - .addRow( - row.new('CPU') - .addPanel( - CPUUtilisation - .addTarget(prometheus.target( - ||| - (( - instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} - * - instance:node_num_cpu:sum{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} - ) != 0 ) - / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) - ||| % $._config, legendFormat='{{ instance }}' - )) - ) - .addPanel( - CPUSaturation - .addTarget(prometheus.target( - ||| - ( - instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} - / scalar(count(instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) - ) != 0 - ||| % $._config, legendFormat='{{instance}}' - )) - ) - ) - .addRow( - row.new('Memory') - .addPanel( - memoryUtilisation - .addTarget(prometheus.target( - ||| - ( - instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} - / scalar(count(instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) - ) != 0 - ||| % $._config, legendFormat='{{instance}}', - )) - ) - .addPanel(memorySaturation.addTarget(prometheus.target('instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"}' % $._config, legendFormat='{{instance}}'))) - ) - .addRow( - row.new('Network') - .addPanel( - networkUtilisation - .addTarget(prometheus.target('instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{instance}} Receive')) - .addTarget(prometheus.target('instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{instance}} Transmit')) - ) - .addPanel( - networkSaturation - .addTarget(prometheus.target('instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{instance}} Receive')) - .addTarget(prometheus.target('instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config, legendFormat='{{instance}} Transmit')) - ) - ) - .addRow( - row.new('Disk IO') - .addPanel( - diskIOUtilisation - .addTarget(prometheus.target( - ||| - ( - instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} - / scalar(count(instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) - ) != 0 - ||| % $._config, legendFormat='{{instance}} {{device}}' - )) - ) - .addPanel( - diskIOSaturation - .addTarget(prometheus.target( - ||| - ( + + dashboard.time.withFrom('now-1h') + + dashboard.withTags($._config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withUid(std.md5('node-cluster-rsrc-use.json')) + + dashboard.withVariables([ + datasource, + $._clusterVariable, + variable.query.withDatasourceFromVariable(datasource) + + variable.query.refresh.onTime() + + variable.query.withSort(asc=true), + ]) + + dashboard.withPanels( + grafana.util.grid.makeGrid([ + row.new('CPU') + + row.withPanels([ + CPUUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + (( + instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} + * + instance:node_num_cpu:sum{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} + ) != 0 ) + / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }}'), + ]), + CPUSaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + ( + instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} + / scalar(count(instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) + ) != 0 + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }}'), + ]), + ]), + row.new('Memory') + + row.withPanels([ + memoryUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + ( + instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} + / scalar(count(instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) + ) != 0 + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }}'), + ]), + memorySaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + 'instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"}' % $._config + ) + prometheus.withLegendFormat('{{ instance }}'), + ]), + ]), + row.new('Network') + + row.withPanels([ + networkUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + 'instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config + ) + prometheus.withLegendFormat('{{ instance }} Receive'), + prometheus.new( + '$datasource', + 'instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config + ) + prometheus.withLegendFormat('{{ instance }} Transmit'), + ]), + networkSaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + 'instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config + ) + prometheus.withLegendFormat('{{ instance }} Receive'), + prometheus.new( + '$datasource', + 'instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} != 0' % $._config + ) + prometheus.withLegendFormat('{{ instance }} Transmit'), + ]), + ]), + row.new('Disk IO') + + row.withPanels([ + diskIOUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} + / scalar(count(instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }} {{device}}'), + ]), + diskIOSaturation + tsQueryOptions.withTargets([prometheus.new( + '$datasource', + ||| instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"} / scalar(count(instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster"})) - ) != 0 - ||| % $._config, legendFormat='{{instance}} {{device}}' - )) - ) - ) - .addRow( - row.new('Disk Space') - .addPanel( - diskSpaceUtilisation - .addTarget(prometheus.target( - ||| - sum without (device) ( - max without (fstype, mountpoint) (( - node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"} - - - node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"} - ) != 0) - ) - / scalar(sum(max without (fstype, mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"}))) - ||| % $._config, legendFormat='{{instance}}' - )) - ) + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }} {{device}}')]), + ]), + ], panelWidth=12, panelHeight=7) + + grafana.util.grid.makeGrid([ + row.new('Disk Space') + + row.withPanels([ + diskSpaceUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum without (device) ( + max without (fstype, mountpoint) (( + node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"} + - + node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"} + ) != 0) + ) + / scalar(sum(max without (fstype, mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s, %(clusterLabel)s="$cluster"}))) + ||| % $._config + ) + prometheus.withLegendFormat('{{ instance }}'), + ]), + ]), + ], panelWidth=24, panelHeight=7, startY=34), ), } + if $._config.showMultiCluster then { 'node-multicluster-rsrc-use.json': dashboard.new( '%sUSE Method / Multi-cluster' % $._config.dashboardNamePrefix, - time_from='now-1h', - tags=($._config.dashboardTags), - timezone='utc', - refresh='30s', - graphTooltip='shared_crosshair', - uid=std.md5('node-multicluster-rsrc-use.json') ) - .addTemplate(datasourceTemplate) - .addRow( - row.new('CPU') - .addPanel( - CPUUtilisation - .addTarget(prometheus.target( - ||| - sum( - (( - instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s} - * - instance:node_num_cpu:sum{%(nodeExporterSelector)s} - ) != 0) - / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s})) - ) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}}' % $._config - )) - ) - .addPanel( - CPUSaturation - .addTarget(prometheus.target( - ||| - sum(( - instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s} - / scalar(count(instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s})) - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}}' % $._config - )) - ) - ) - .addRow( - row.new('Memory') - .addPanel( - memoryUtilisation - .addTarget(prometheus.target( - ||| - sum(( - instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s} - / scalar(count(instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s})) - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}}' % $._config - )) - ) - .addPanel( - memorySaturation - .addTarget(prometheus.target( - ||| - sum(( - instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s} - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}}' % $._config - )) - ) - ) - .addRow( - row.new('Network') - .addPanel( - networkUtilisation - .addTarget(prometheus.target( - ||| - sum(( - instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} Receive' % $._config - )) - .addTarget(prometheus.target( - ||| - sum(( - instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} Transmit' % $._config - )) - ) - .addPanel( - networkSaturation - .addTarget(prometheus.target( - ||| - sum(( - instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} Receive' % $._config - )) - .addTarget(prometheus.target( - ||| - sum(( - instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} - ) != 0) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} Transmit' % $._config - )) - ) - ) - .addRow( - row.new('Disk IO') - .addPanel( - diskIOUtilisation - .addTarget(prometheus.target( - ||| - sum(( - instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s} - / scalar(count(instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s})) - ) != 0) by (%(clusterLabel)s, device) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} {{device}}' % $._config - )) - ) - .addPanel( - diskIOSaturation - .addTarget(prometheus.target( - ||| - sum(( - instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s} - / scalar(count(instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s})) - ) != 0) by (%(clusterLabel)s, device) - ||| % $._config, legendFormat='{{%(clusterLabel)s}} {{device}}' % $._config - )) - ) - ) - .addRow( - row.new('Disk Space') - .addPanel( - diskSpaceUtilisation - .addTarget(prometheus.target( - ||| - sum ( - sum without (device) ( - max without (fstype, mountpoint, instance, pod) (( - node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s} - node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s} - ) != 0) - ) - / scalar(sum(max without (fstype, mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s}))) - ) by (%(clusterLabel)s) - ||| % $._config, legendFormat='{{%(clusterLabel)s}}' % $._config - )) - ) + + dashboard.time.withFrom('now-1h') + + dashboard.withTags($._config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withUid(std.md5('node-multicluster-rsrc-use.json')) + + dashboard.withVariables([ + datasource, + variable.query.withDatasourceFromVariable(datasource) + + variable.query.refresh.onTime() + + variable.query.withSort(asc=true), + ]) + + dashboard.withPanels( + grafana.util.grid.makeGrid([ + row.new('CPU') + + row.withPanels([ + CPUUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum( + (( + instance:node_cpu_utilisation:rate%(rateInterval)s{%(nodeExporterSelector)s} + * + instance:node_num_cpu:sum{%(nodeExporterSelector)s} + ) != 0) + / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s})) + ) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}}'), + ]), + CPUSaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s} + / scalar(count(instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s})) + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}}'), + ]), + ]), + row.new('Memory') + + row.withPanels([ + memoryUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s} + / scalar(count(instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s})) + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}}'), + ]), + memorySaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_vmstat_pgmajfault:rate%(rateInterval)s{%(nodeExporterSelector)s} + ) != 0) by (%(clusterLabel)s) + ||| + % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}}'), + ]), + ]), + row.new('Network') + + row.withPanels([ + networkUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_network_receive_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} Receive'), + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_network_transmit_bytes_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} Transmit'), + ]), + networkSaturation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_network_receive_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} Receive'), + prometheus.new( + '$datasource', + ||| + sum(( + instance:node_network_transmit_drop_excluding_lo:rate%(rateInterval)s{%(nodeExporterSelector)s} + ) != 0) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} Transmit'), + ]), + ]), + row.new('Disk IO') + + row.withPanels([ + diskIOUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum(( + instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s} + / scalar(count(instance_device:node_disk_io_time_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s})) + ) != 0) by (%(clusterLabel)s, device) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} {{device}}'), + ]), + diskIOSaturation + tsQueryOptions.withTargets([prometheus.new( + '$datasource', + ||| + sum(( + instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s} + / scalar(count(instance_device:node_disk_io_time_weighted_seconds:rate%(rateInterval)s{%(nodeExporterSelector)s})) + ) != 0) by (%(clusterLabel)s, device) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}} {{device}}')]), + ]), + + ], panelWidth=12, panelHeight=7) + + grafana.util.grid.makeGrid([ + row.new('Disk Space') + + row.withPanels([ + diskSpaceUtilisation + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + sum ( + sum without (device) ( + max without (fstype, mountpoint, instance, pod) (( + node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s} - node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s} + ) != 0) + ) + / scalar(sum(max without (fstype, mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s, %(fsMountpointSelector)s}))) + ) by (%(clusterLabel)s) + ||| % $._config + ) + prometheus.withLegendFormat('{{%(clusterLabel)s}}'), + ]), + ]), + ], panelWidth=24, panelHeight=7, startY=34), ), } else {}, } diff --git a/docs/node-mixin/jsonnetfile.json b/docs/node-mixin/jsonnetfile.json index 721d4833..2d56d912 100644 --- a/docs/node-mixin/jsonnetfile.json +++ b/docs/node-mixin/jsonnetfile.json @@ -4,20 +4,11 @@ { "source": { "git": { - "remote": "https://github.com/grafana/grafonnet-lib.git", - "subdir": "grafonnet" + "remote": "https://github.com/grafana/grafonnet.git", + "subdir": "gen/grafonnet-latest" } }, - "version": "master" - }, - { - "source": { - "git": { - "remote": "https://github.com/grafana/grafonnet-lib.git", - "subdir": "grafonnet-7.0" - } - }, - "version": "master" + "version": "main" } ], "legacyImports": false diff --git a/docs/node-mixin/lib/prom-mixin.libsonnet b/docs/node-mixin/lib/prom-mixin.libsonnet index 94f1b76a..f18c273c 100644 --- a/docs/node-mixin/lib/prom-mixin.libsonnet +++ b/docs/node-mixin/lib/prom-mixin.libsonnet @@ -1,523 +1,535 @@ -local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet'; +local grafana = import 'github.com/grafana/grafonnet/gen/grafonnet-latest/main.libsonnet'; local dashboard = grafana.dashboard; -local row = grafana.row; -local prometheus = grafana.prometheus; -local template = grafana.template; -local graphPanel = grafana.graphPanel; -local grafana70 = import 'github.com/grafana/grafonnet-lib/grafonnet-7.0/grafana.libsonnet'; -local gaugePanel = grafana70.panel.gauge; -local table = grafana70.panel.table; +local row = grafana.panel.row; +local prometheus = grafana.query.prometheus; +local variable = dashboard.variable; + +local timeSeriesPanel = grafana.panel.timeSeries; +local tsOptions = timeSeriesPanel.options; +local tsStandardOptions = timeSeriesPanel.standardOptions; +local tsQueryOptions = timeSeriesPanel.queryOptions; +local tsCustom = timeSeriesPanel.fieldConfig.defaults.custom; + +local gaugePanel = grafana.panel.gauge; +local gaugeStep = gaugePanel.standardOptions.threshold.step; + +local table = grafana.panel.table; +local tableStep = table.standardOptions.threshold.step; +local tableOverride = table.standardOptions.override; +local tableTransformation = table.queryOptions.transformation; { new(config=null, platform=null, uid=null):: { - local prometheusDatasourceTemplate = { - current: { - text: 'default', - value: 'default', - }, - hide: 0, - label: 'Data Source', - name: 'datasource', - options: [], - query: 'prometheus', - refresh: 1, - regex: '', - type: 'datasource', - }, + local prometheusDatasourceVariable = variable.datasource.new( + 'datasource', 'prometheus' + ), - local clusterTemplatePrototype = - template.new( - 'cluster', - '$datasource', - '', - hide=if config.showMultiCluster then '' else '2', - refresh='time', - label='Cluster', - ), - local clusterTemplate = - if platform == 'Darwin' then - clusterTemplatePrototype - { query: 'label_values(node_uname_info{%(nodeExporterSelector)s, sysname="Darwin"}, %(clusterLabel)s)' % config } - else - clusterTemplatePrototype - { query: 'label_values(node_uname_info{%(nodeExporterSelector)s, sysname!="Darwin"}, %(clusterLabel)s)' % config }, + local clusterVariablePrototype = + variable.query.new('cluster') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + (if config.showMultiCluster then variable.query.generalOptions.showOnDashboard.withLabelAndValue() else variable.query.generalOptions.showOnDashboard.withNothing()) + + variable.query.refresh.onTime() + + variable.query.generalOptions.withLabel('Cluster'), - local instanceTemplatePrototype = - template.new( - 'instance', - '$datasource', - '', - refresh='time', - label='Instance', - ), - local instanceTemplate = + local clusterVariable = if platform == 'Darwin' then - instanceTemplatePrototype - { query: 'label_values(node_uname_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster", sysname="Darwin"}, instance)' % config } + clusterVariablePrototype + + variable.query.queryTypes.withLabelValues( + ' %(clusterLabel)s' % config, + 'node_uname_info{%(nodeExporterSelector)s, sysname="Darwin"}' % config, + ) else - instanceTemplatePrototype - { query: 'label_values(node_uname_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster", sysname!="Darwin"}, instance)' % config }, + clusterVariablePrototype + + variable.query.queryTypes.withLabelValues( + '%(clusterLabel)s' % config, + 'node_uname_info{%(nodeExporterSelector)s, sysname!="Darwin"}' % config, + ), + + local instanceVariablePrototype = + variable.query.new('instance') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + variable.query.refresh.onTime() + + variable.query.generalOptions.withLabel('Instance'), + + local instanceVariable = + if platform == 'Darwin' then + instanceVariablePrototype + + variable.query.queryTypes.withLabelValues( + 'instance', + 'node_uname_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster", sysname="Darwin"}' % config, + ) + else + instanceVariablePrototype + + variable.query.queryTypes.withLabelValues( + 'instance', + 'node_uname_info{%(nodeExporterSelector)s, %(clusterLabel)s="$cluster", sysname!="Darwin"}' % config, + ), local idleCPU = - graphPanel.new( - 'CPU Usage', - datasource='$datasource', - span=6, - format='percentunit', - max=1, - min=0, - stack=true, - ) - .addTarget(prometheus.target( - ||| - ( - (1 - sum without (mode) (rate(node_cpu_seconds_total{%(nodeExporterSelector)s, mode=~"idle|iowait|steal", instance="$instance", %(clusterLabel)s="$cluster"}[$__rate_interval]))) - / ignoring(cpu) group_left - count without (cpu, mode) (node_cpu_seconds_total{%(nodeExporterSelector)s, mode="idle", instance="$instance", %(clusterLabel)s="$cluster"}) - ) - ||| % config, - legendFormat='{{cpu}}', - intervalFactor=5, - )), - - local systemLoad = - graphPanel.new( - 'Load Average', - datasource='$datasource', - span=6, - format='short', - min=0, - fill=0, - ) - .addTarget(prometheus.target('node_load1{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='1m load average')) - .addTarget(prometheus.target('node_load5{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='5m load average')) - .addTarget(prometheus.target('node_load15{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='15m load average')) - .addTarget(prometheus.target('count(node_cpu_seconds_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", mode="idle"})' % config, legendFormat='logical cores')), - - local memoryGraphPanelPrototype = - graphPanel.new( - 'Memory Usage', - datasource='$datasource', - span=9, - format='bytes', - min=0, - ), - local memoryGraph = - if platform == 'Linux' then - memoryGraphPanelPrototype { stack: true } - .addTarget(prometheus.target( + timeSeriesPanel.new('CPU Usage') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withUnit('percentunit') + + tsCustom.stacking.withMode('normal') + + tsStandardOptions.withMax(1) + + tsStandardOptions.withMin(0) + + tsOptions.tooltip.withMode('multi') + + tsCustom.withFillOpacity(10) + + tsCustom.withShowPoints('never') + + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', ||| ( - node_memory_MemTotal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - - - node_memory_MemFree_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - - - node_memory_Buffers_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - - - node_memory_Cached_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + (1 - sum without (mode) (rate(node_cpu_seconds_total{%(nodeExporterSelector)s, mode=~"idle|iowait|steal", instance="$instance", %(clusterLabel)s="$cluster"}[$__rate_interval]))) + / ignoring(cpu) group_left + count without (cpu, mode) (node_cpu_seconds_total{%(nodeExporterSelector)s, mode="idle", instance="$instance", %(clusterLabel)s="$cluster"}) ) ||| % config, - legendFormat='memory used' - )) - .addTarget(prometheus.target('node_memory_Buffers_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='memory buffers')) - .addTarget(prometheus.target('node_memory_Cached_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='memory cached')) - .addTarget(prometheus.target('node_memory_MemFree_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config, legendFormat='memory free')) + ) + + prometheus.withLegendFormat('{{cpu}}') + + prometheus.withIntervalFactor(5), + ]), + + local systemLoad = + timeSeriesPanel.new('Load Average') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withUnit('short') + + tsStandardOptions.withMin(0) + + tsCustom.withFillOpacity(0) + + tsCustom.withShowPoints('never') + + tsOptions.tooltip.withMode('multi') + + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'node_load1{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('1m load average'), + prometheus.new('$datasource', 'node_load5{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('5m load average'), + prometheus.new('$datasource', 'node_load15{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('15m load average'), + prometheus.new('$datasource', 'count(node_cpu_seconds_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", mode="idle"})' % config) + prometheus.withLegendFormat('logical cores'), + ]), + + local memoryGraphPanelPrototype = + timeSeriesPanel.new('Memory Usage') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withUnit('bytes') + + tsStandardOptions.withMin(0) + + tsOptions.tooltip.withMode('multi') + + tsCustom.withFillOpacity(10) + + tsCustom.withShowPoints('never'), + + local memoryGraph = + if platform == 'Linux' then + memoryGraphPanelPrototype + + tsCustom.stacking.withMode('normal') + + tsQueryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + ( + node_memory_MemTotal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + - + node_memory_MemFree_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + - + node_memory_Buffers_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + - + node_memory_Cached_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + ) + ||| % config, + ) + prometheus.withLegendFormat('memory used'), + prometheus.new('$datasource', 'node_memory_Buffers_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('memory buffers'), + prometheus.new('$datasource', 'node_memory_Cached_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('memory cached'), + prometheus.new('$datasource', 'node_memory_MemFree_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('memory free'), + ]) else if platform == 'Darwin' then // not useful to stack - 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_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - - node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + - node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + - node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - ) - ||| % config, legendFormat='Memory Used' - )) - .addTarget(prometheus.target( - ||| - ( - node_memory_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - - node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - ) - ||| % 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')), + memoryGraphPanelPrototype + + tsCustom.stacking.withMode('none') + + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('Physical Memory'), + prometheus.new( + '$datasource', + ||| + ( + node_memory_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - + node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + + node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + + node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + ) + ||| % config + ) + prometheus.withLegendFormat( + 'Memory Used' + ), + prometheus.new( + '$datasource', + ||| + ( + node_memory_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - + node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + ) + ||| % config + ) + prometheus.withLegendFormat( + 'App Memory' + ), + prometheus.new('$datasource', 'node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('Wired Memory'), + prometheus.new('$datasource', 'node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('Compressed'), + ]) + + else if platform == 'AIX' then + memoryGraphPanelPrototype + + tsCustom.stacking.withMode('none') + + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}' % config) + prometheus.withLegendFormat('Physical Memory'), + prometheus.new( + '$datasource', + ||| + ( + node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} - + node_memory_available_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"} + ) + ||| % config + ) + prometheus.withLegendFormat('Memory Used'), + ]), + // NOTE: avg() is used to circumvent a label change caused by a node_exporter rollout. local memoryGaugePanelPrototype = - gaugePanel.new( - title='Memory Usage', - datasource='$datasource', - ) - .addThresholdStep('rgba(50, 172, 45, 0.97)') - .addThresholdStep('rgba(237, 129, 40, 0.89)', 80) - .addThresholdStep('rgba(245, 54, 54, 0.9)', 90) - .setFieldConfig(max=100, min=0, unit='percent') - + { - span: 3, - }, + gaugePanel.new('Memory Usage') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + gaugePanel.standardOptions.thresholds.withSteps([ + gaugeStep.withColor('rgba(50, 172, 45, 0.97)'), + gaugeStep.withColor('rgba(237, 129, 40, 0.89)') + gaugeStep.withValue(80), + gaugeStep.withColor('rgba(245, 54, 54, 0.9)') + gaugeStep.withValue(90), + ]) + + gaugePanel.standardOptions.withMax(100) + + gaugePanel.standardOptions.withMin(0) + + gaugePanel.standardOptions.withUnit('percent'), local memoryGauge = if platform == 'Linux' then memoryGaugePanelPrototype - - .addTarget(prometheus.target( - ||| - 100 - - ( - avg(node_memory_MemAvailable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) / - avg(node_memory_MemTotal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) - * 100 - ) - ||| % config, - )) + + gaugePanel.queryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + 100 - + ( + avg(node_memory_MemAvailable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) / + avg(node_memory_MemTotal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + * 100 + ) + ||| % config, + ), + ]) else if platform == 'Darwin' then memoryGaugePanelPrototype - .addTarget(prometheus.target( - ||| - ( - ( - avg(node_memory_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) - - avg(node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + - avg(node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + - avg(node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) - ) / + + gaugePanel.queryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + ( + ( + avg(node_memory_internal_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) - + avg(node_memory_purgeable_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + + avg(node_memory_wired_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + + avg(node_memory_compressed_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + ) / + avg(node_memory_total_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster"}) + ) + * + 100 + ||| % config + ), + ]) + + else if platform == 'AIX' then + memoryGaugePanelPrototype + + gaugePanel.queryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + 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 - )), + * 100 + ) + ||| % config + ), + ]), + local diskIO = - graphPanel.new( - 'Disk I/O', - datasource='$datasource', - span=6, - min=0, - fill=0, - ) - // TODO: Does it make sense to have those three in the same panel? - .addTarget(prometheus.target( - 'rate(node_disk_read_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config, - legendFormat='{{device}} read', - intervalFactor=1, - )) - .addTarget(prometheus.target( - 'rate(node_disk_written_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config, - legendFormat='{{device}} written', - intervalFactor=1, - )) - .addTarget(prometheus.target( - 'rate(node_disk_io_time_seconds_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config, - legendFormat='{{device}} io time', - intervalFactor=1, - )) + - { - seriesOverrides: [ - { - alias: '/ read| written/', - yaxis: 1, - }, - { - alias: '/ io time/', - yaxis: 2, - }, - ], - yaxes: [ - self.yaxe(format='Bps'), - self.yaxe(format='percentunit'), - ], - }, + timeSeriesPanel.new('Disk I/O') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withMin(0) + + tsCustom.withFillOpacity(0) + + tsCustom.withShowPoints('never') + + tsOptions.tooltip.withMode('multi') + + tsQueryOptions.withTargets([ + // TODO: Does it make sense to have those three in the same panel? + prometheus.new('$datasource', 'rate(node_disk_read_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config) + + prometheus.withLegendFormat('{{device}} read') + + prometheus.withIntervalFactor(1), + prometheus.new('$datasource', 'rate(node_disk_written_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config) + + prometheus.withLegendFormat('{{device}} written') + + prometheus.withIntervalFactor(1), + prometheus.new('$datasource', 'rate(node_disk_io_time_seconds_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(diskDeviceSelector)s}[$__rate_interval])' % config) + + prometheus.withLegendFormat('{{device}} io time') + + prometheus.withIntervalFactor(1), + ]) + + tsStandardOptions.withOverrides( + [ + tsStandardOptions.override.byRegexp.new('/ read| written/') + + tsStandardOptions.override.byRegexp.withPropertiesFromOptions( + tsStandardOptions.withUnit('Bps') + ), + tsStandardOptions.override.byRegexp.new('/ io time/') + + tsStandardOptions.override.byRegexp.withPropertiesFromOptions(tsStandardOptions.withUnit('percentunit')), + ] + ), local diskSpaceUsage = - table.new( - title='Disk Space Usage', - datasource='$datasource', - ) - .setFieldConfig(unit='decbytes') - .addThresholdStep(color='green', value=null) - .addThresholdStep(color='yellow', value=0.8) - .addThresholdStep(color='red', value=0.9) - .addTarget(prometheus.target( - ||| - max by (mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(fsSelector)s, %(fsMountpointSelector)s}) - ||| % config, - legendFormat='', - instant=true, - format='table' - )) - .addTarget(prometheus.target( - ||| - max by (mountpoint) (node_filesystem_avail_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(fsSelector)s, %(fsMountpointSelector)s}) - ||| % config, - legendFormat='', - instant=true, - format='table' - )) - .addOverride( - matcher={ - id: 'byName', - options: 'Mounted on', - }, - properties=[ - { - id: 'custom.width', - value: 260, - }, - ], - ) - .addOverride( - matcher={ - id: 'byName', - options: 'Size', - }, - properties=[ - - { - id: 'custom.width', - value: 93, - }, - - ], - ) - .addOverride( - matcher={ - id: 'byName', - options: 'Used', - }, - properties=[ - { - id: 'custom.width', - value: 72, - }, - ], - ) - .addOverride( - matcher={ - id: 'byName', - options: 'Available', - }, - properties=[ - { - id: 'custom.width', - value: 88, - }, - ], - ) - - .addOverride( - matcher={ - id: 'byName', - options: 'Used, %', - }, - properties=[ - { - id: 'unit', - value: 'percentunit', - }, - { - id: 'custom.displayMode', - value: 'gradient-gauge', - }, - { - id: 'max', - value: 1, - }, - { - id: 'min', - value: 0, - }, + table.new('Disk Space Usage') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + table.standardOptions.withUnit('decbytes') + + table.standardOptions.thresholds.withSteps( + [ + tableStep.withColor('green'), + tableStep.withColor('yellow') + gaugeStep.withValue(0.8), + tableStep.withColor('red') + gaugeStep.withValue(0.9), ] ) - + { span: 6 } - + { - transformations: [ + + table.queryOptions.withTargets([ + prometheus.new( + '$datasource', + ||| + max by (mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(fsSelector)s, %(fsMountpointSelector)s}) + ||| % config + ) + + prometheus.withLegendFormat('') + + prometheus.withInstant() + + prometheus.withFormat('table'), + prometheus.new( + '$datasource', + ||| + max by (mountpoint) (node_filesystem_avail_bytes{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", %(fsSelector)s, %(fsMountpointSelector)s}) + ||| % config + ) + + prometheus.withLegendFormat('') + + prometheus.withInstant() + + prometheus.withFormat('table'), + ]) + + table.standardOptions.withOverrides([ + tableOverride.byName.new('Mounted on') + + tableOverride.byName.withProperty('custom.width', 260), + tableOverride.byName.new('Size') + + tableOverride.byName.withProperty('custom.width', 93), + tableOverride.byName.new('Used') + + tableOverride.byName.withProperty('custom.width', 72), + tableOverride.byName.new('Available') + + tableOverride.byName.withProperty('custom.width', 88), + tableOverride.byName.new('Used, %') + + tableOverride.byName.withProperty('unit', 'percentunit') + + tableOverride.byName.withPropertiesFromOptions( + table.fieldConfig.defaults.custom.withCellOptions( + { type: 'gauge' }, + ) + ) + + tableOverride.byName.withProperty('max', 1) + + tableOverride.byName.withProperty('min', 0), + ]) + + table.queryOptions.withTransformations([ + tableTransformation.withId('groupBy') + + tableTransformation.withOptions( { - id: 'groupBy', - options: { - fields: { - 'Value #A': { - aggregations: [ - 'lastNotNull', - ], - operation: 'aggregate', - }, - 'Value #B': { - aggregations: [ - 'lastNotNull', - ], - operation: 'aggregate', - }, - mountpoint: { - aggregations: [], - operation: 'groupby', - }, + fields: { + 'Value #A': { + aggregations: [ + 'lastNotNull', + ], + operation: 'aggregate', + }, + 'Value #B': { + aggregations: [ + 'lastNotNull', + ], + operation: 'aggregate', + }, + mountpoint: { + aggregations: [], + operation: 'groupby', }, }, - }, + } + ), + tableTransformation.withId('merge'), + tableTransformation.withId('calculateField') + + tableTransformation.withOptions( { - id: 'merge', - options: {}, - }, - { - id: 'calculateField', - options: { - alias: 'Used', - binary: { - left: 'Value #A (lastNotNull)', - operator: '-', - reducer: 'sum', - right: 'Value #B (lastNotNull)', - }, - mode: 'binary', - reduce: { - reducer: 'sum', - }, + alias: 'Used', + binary: { + left: 'Value #A (lastNotNull)', + operator: '-', + reducer: 'sum', + right: 'Value #B (lastNotNull)', }, - }, - { - id: 'calculateField', - options: { - alias: 'Used, %', - binary: { - left: 'Used', - operator: '/', - reducer: 'sum', - right: 'Value #A (lastNotNull)', - }, - mode: 'binary', - reduce: { - reducer: 'sum', - }, + mode: 'binary', + reduce: { + reducer: 'sum', }, - }, + } + ), + tableTransformation.withId('calculateField') + + tableTransformation.withOptions( { - id: 'organize', - options: { - excludeByName: {}, - indexByName: {}, - renameByName: { - 'Value #A (lastNotNull)': 'Size', - 'Value #B (lastNotNull)': 'Available', - mountpoint: 'Mounted on', + alias: 'Used, %', + binary: { + left: 'Used', + operator: '/', + reducer: 'sum', + right: 'Value #A (lastNotNull)', + }, + mode: 'binary', + reduce: { + reducer: 'sum', + }, + } + ), + tableTransformation.withId('organize') + + tableTransformation.withOptions( + { + excludeByName: {}, + indexByName: {}, + renameByName: { + 'Value #A (lastNotNull)': 'Size', + 'Value #B (lastNotNull)': 'Available', + mountpoint: 'Mounted on', + }, + } + ), + tableTransformation.withId('sortBy') + + tableTransformation.withOptions( + { + fields: {}, + sort: [ + { + field: 'Mounted on', }, - }, - }, - { - id: 'sortBy', - options: { - fields: {}, - sort: [ - { - field: 'Mounted on', - }, - ], - }, - }, - ], - }, + ], + } + ), + ]), local networkReceived = - graphPanel.new( - 'Network Received', - description='Network received (bits/s)', - datasource='$datasource', - span=6, - format='bps', - min=0, - fill=0, - ) - .addTarget(prometheus.target( - 'rate(node_network_receive_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", device!="lo"}[$__rate_interval]) * 8' % config, - legendFormat='{{device}}', - intervalFactor=1, - )), + timeSeriesPanel.new('Network Received') + + timeSeriesPanel.panelOptions.withDescription('Network received (bits/s)') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withUnit('bps') + + tsStandardOptions.withMin(0) + + tsCustom.withFillOpacity(0) + + tsCustom.withShowPoints('never') + + tsOptions.tooltip.withMode('multi') + + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'rate(node_network_receive_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", device!="lo"}[$__rate_interval]) * 8' % config) + + prometheus.withLegendFormat('{{device}}') + + prometheus.withIntervalFactor(1), + ]), local networkTransmitted = - graphPanel.new( - 'Network Transmitted', - description='Network transmitted (bits/s)', - datasource='$datasource', - span=6, - format='bps', - min=0, - fill=0, - ) - .addTarget(prometheus.target( - 'rate(node_network_transmit_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", device!="lo"}[$__rate_interval]) * 8' % config, - legendFormat='{{device}}', - intervalFactor=1, - )), + timeSeriesPanel.new('Network Transmitted') + + timeSeriesPanel.panelOptions.withDescription('Network transmitted (bits/s)') + + variable.query.withDatasourceFromVariable(prometheusDatasourceVariable) + + tsStandardOptions.withUnit('bps') + + tsStandardOptions.withMin(0) + + tsCustom.withFillOpacity(0) + + tsOptions.tooltip.withMode('multi') + + tsQueryOptions.withTargets([ + prometheus.new('$datasource', 'rate(node_network_transmit_bytes_total{%(nodeExporterSelector)s, instance="$instance", %(clusterLabel)s="$cluster", device!="lo"}[$__rate_interval]) * 8' % config) + + prometheus.withLegendFormat('{{device}}') + + prometheus.withIntervalFactor(1), + ]), local cpuRow = row.new('CPU') - .addPanel(idleCPU) - .addPanel(systemLoad), + + row.withPanels([ + idleCPU, + systemLoad, + ]), - local memoryRow = - row.new('Memory') - .addPanel(memoryGraph) - .addPanel(memoryGauge), + local memoryRow = [ + row.new('Memory') + row.gridPos.withY(8), + memoryGraph + row.gridPos.withX(0) + row.gridPos.withY(9) + row.gridPos.withH(7) + row.gridPos.withW(18), + memoryGauge + row.gridPos.withX(18) + row.gridPos.withY(9) + row.gridPos.withH(7) + row.gridPos.withW(6), + ], local diskRow = row.new('Disk') - .addPanel(diskIO) - .addPanel(diskSpaceUsage), + + row.withPanels([ + diskIO, + diskSpaceUsage, + ]), local networkRow = row.new('Network') - .addPanel(networkReceived) - .addPanel(networkTransmitted), + + row.withPanels([ + networkReceived, + networkTransmitted, + ]), - local rows = - [ + local panels = + grafana.util.grid.makeGrid([ cpuRow, - memoryRow, + ], panelWidth=12, panelHeight=7) + + memoryRow + + grafana.util.grid.makeGrid([ diskRow, networkRow, - ], + ], panelWidth=12, panelHeight=7, startY=18), - local templates = + local variables = [ - prometheusDatasourceTemplate, - clusterTemplate, - instanceTemplate, + prometheusDatasourceVariable, + clusterVariable, + instanceVariable, ], - dashboard: if platform == 'Linux' then dashboard.new( '%sNodes' % config.dashboardNamePrefix, - time_from='now-1h', - tags=(config.dashboardTags), - timezone='utc', - refresh='30s', - graphTooltip='shared_crosshair', - uid=std.md5(uid) ) - .addTemplates(templates) - .addRows(rows) + + dashboard.time.withFrom('now-1h') + + dashboard.withTags(config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.withUid(std.md5(uid)) + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withVariables(variables) + + dashboard.withPanels(panels) else if platform == 'Darwin' then dashboard.new( '%sMacOS' % config.dashboardNamePrefix, - time_from='now-1h', - tags=(config.dashboardTags), - timezone='utc', - refresh='30s', - graphTooltip='shared_crosshair', - uid=std.md5(uid) ) - .addTemplates(templates) - .addRows(rows), + + dashboard.time.withFrom('now-1h') + + dashboard.withTags(config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.withUid(std.md5(uid)) + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withVariables(variables) + + dashboard.withPanels(panels) + else if platform == 'AIX' then + dashboard.new( + '%sAIX' % config.dashboardNamePrefix, + ) + + dashboard.time.withFrom('now-1h') + + dashboard.withTags(config.dashboardTags) + + dashboard.withTimezone('utc') + + dashboard.withRefresh('30s') + + dashboard.withUid(std.md5(uid)) + + dashboard.graphTooltip.withSharedCrosshair() + + dashboard.withVariables(variables) + + dashboard.withPanels(panels), }, } diff --git a/end-to-end-test.sh b/end-to-end-test.sh index 35e4534e..944bdc0e 100755 --- a/end-to-end-test.sh +++ b/end-to-end-test.sh @@ -2,6 +2,38 @@ set -euf -o pipefail +# Allow setting GOHOSTOS for debugging purposes. +GOHOSTOS=${GOHOSTOS:-$(go env GOHOSTOS)} + +# Allow setting arch for debugging purposes. +arch=${arch:-$(uname -m)} + +maybe_flag_search_scope() { + local collector=$1 + os_aux_os="" + if [[ $GOHOSTOS =~ ^(freebsd|openbsd|netbsd|solaris|dragonfly)$ ]]; then + os_aux_os=" ${collector}_bsd.go" + fi + echo "${collector}_common.go ${collector}.go ${collector}_${GOHOSTOS}.go ${collector}_${GOHOSTOS}_${arch}.go${os_aux_os}" +} + +supported_collectors() { + local collectors=$1 + local supported="" + for collector in ${collectors}; do + for filename in $(maybe_flag_search_scope "${collector}"); do + file="collector/${filename}" + if ./tools/tools match ${file} > /dev/null 2>&1; then + if grep -h -E -o -- "registerCollector\(" ${file} > /dev/null 2>&1; then + supported="${supported} ${collector}" + fi + break + fi + done + done + echo "${supported}" | tr ' ' '\n' | sort | uniq +} + enabled_collectors=$(cat << COLLECTORS arp bcache @@ -57,6 +89,8 @@ enabled_collectors=$(cat << COLLECTORS zoneinfo COLLECTORS ) +supported_enabled_collectors=$(supported_collectors "${enabled_collectors}") + disabled_collectors=$(cat << COLLECTORS selinux filesystem @@ -64,6 +98,8 @@ disabled_collectors=$(cat << COLLECTORS uname COLLECTORS ) +supported_disabled_collectors=$(supported_collectors "${disabled_collectors}") + cd "$(dirname $0)" port="$((10000 + (RANDOM % 10000)))" @@ -71,11 +107,9 @@ tmpdir=$(mktemp -d /tmp/node_exporter_e2e_test.XXXXXX) skip_re="^(go_|node_exporter_build_info|node_scrape_collector_duration_seconds|process_|node_textfile_mtime_seconds|node_time_(zone|seconds)|node_network_(receive|transmit)_(bytes|packets)_total)" -arch="$(uname -m)" - case "${arch}" in - aarch64|ppc64le) fixture='collector/fixtures/e2e-64k-page-output.txt' ;; - *) fixture='collector/fixtures/e2e-output.txt' ;; + aarch64|ppc64le) fixture_metrics='collector/fixtures/e2e-64k-page-output.txt' ;; + *) fixture_metrics='collector/fixtures/e2e-output.txt' ;; esac # Only test CPU info collection on x86_64. @@ -109,7 +143,7 @@ do *) echo "Usage: $0 [-k] [-u] [-v]" echo " -k: keep temporary files and leave node_exporter running" - echo " -u: update fixture" + echo " -u: update fixture_metrics" echo " -v: verbose output" exit 1 ;; @@ -122,37 +156,126 @@ then exit 1 fi +collector_flags=$(cat << FLAGS + ${cpu_info_collector} + --collector.arp.device-exclude=nope + --collector.bcache.priorityStats + --collector.cpu.info.bugs-include=${cpu_info_bugs} + --collector.cpu.info.flags-include=${cpu_info_flags} + --collector.hwmon.chip-include=(applesmc|coretemp|hwmon4|nct6779) + --collector.netclass.ignore-invalid-speed + --collector.netclass.ignored-devices=(dmz|int) + --collector.netdev.device-include=lo + --collector.qdisc.device-include=(wlan0|eth0) + --collector.qdisc.fixtures=collector/fixtures/qdisc/ + --collector.stat.softirq + --collector.sysctl.include-info=kernel.seccomp.actions_avail + --collector.sysctl.include=fs.file-nr + --collector.sysctl.include=fs.file-nr:total,current,max + --collector.sysctl.include=kernel.threads-max + --collector.textfile.directory=collector/fixtures/textfile/two_metric_files/ + --collector.wifi.fixtures=collector/fixtures/wifi + --no-collector.arp.netlink +FLAGS +) + +# Handle supported --[no-]collector. flags. These are not hardcoded. +_filtered_collector_flags="" +for flag in ${collector_flags}; do + collector=$(echo "${flag}" | cut -d"." -f2) + # If the flag is associated with an enabled-by-default collector, include it. + enabled_by_default=0 + for filename in $(maybe_flag_search_scope "${collector}") ; do + file="collector/${filename}" + if grep -h -E -o -- "registerCollector\(.*, defaultEnabled" ${file} > /dev/null 2>&1; then + _filtered_collector_flags="${_filtered_collector_flags} ${flag}" + enabled_by_default=1 + break + fi + done + if [ ${enabled_by_default} -eq 1 ]; then + continue + fi + # If the flag is associated with an enabled-list collector, include it. + if echo "${supported_enabled_collectors} ${supported_disabled_collectors}" | grep -q -w "${collector}"; then + _filtered_collector_flags="${_filtered_collector_flags} ${flag}" + fi +done + +# Handle supported --[no-]collector.. flags. These are hardcoded and matched by the expression below. +filtered_collector_flags="" +# Check flags of all supported collectors further down their sub-collectors (beyond the 2nd "."). +for flag in ${_filtered_collector_flags}; do + # Iterate through all possible files where the flag may be defined. + flag_collector="$(echo "${flag}" | cut -d"." -f2)" + for filename in $(maybe_flag_search_scope "${flag_collector}") ; do + file="collector/${filename}" + # Move to next iteration if the current file is not included under the build context. + if ! ./tools/tools match "$file" > /dev/null 2>&1; then + continue + fi + # Flag has the format: --[no-]collector... + if [ -n "$(echo ${flag} | cut -d"." -f3)" ]; then + # Check if the flag is used in the file. + trimmed_flag=$(echo "${flag}" | tr -d "\"' " | cut -d"=" -f1 | cut -c 3-) + if [[ $trimmed_flag =~ ^no- ]]; then + trimmed_flag=$(echo $trimmed_flag | cut -c 4-) + fi + if grep -h -E -o -- "kingpin.Flag\(\"${trimmed_flag}" ${file} > /dev/null 2>&1; then + filtered_collector_flags="${filtered_collector_flags} ${flag}" + else + continue + fi + # Flag has the format: --[no-]collector.. + else + # Flag is supported by the host. + filtered_collector_flags="${filtered_collector_flags} ${flag}" + fi + done +done + +# Check for ignored flags. +ignored_flags="" +for flag in ${collector_flags}; do + flag=$(echo "${flag}" | tr -d " ") + if ! echo "${filtered_collector_flags}" | grep -q -F -- "${flag}" > /dev/null 2>&1; then + ignored_flags="${ignored_flags} ${flag}" + fi +done + +echo "ENABLED COLLECTORS=======" +echo "${supported_enabled_collectors:1}" | tr ' ' '\n' | sort +echo "=========================" + +echo "DISABLED COLLECTORS======" +echo "${supported_disabled_collectors:1}" | tr ' ' '\n' | sort +echo "=========================" + +echo "IGNORED FLAGS============" +echo "${ignored_flags:1}"| tr ' ' '\n' | sort | uniq +echo "=========================" + ./node_exporter \ --path.rootfs="collector/fixtures" \ --path.procfs="collector/fixtures/proc" \ --path.sysfs="collector/fixtures/sys" \ --path.udev.data="collector/fixtures/udev/data" \ - $(for c in ${enabled_collectors}; do echo --collector.${c} ; done) \ - $(for c in ${disabled_collectors}; do echo --no-collector.${c} ; done) \ - --collector.textfile.directory="collector/fixtures/textfile/two_metric_files/" \ - --collector.wifi.fixtures="collector/fixtures/wifi" \ - --collector.qdisc.fixtures="collector/fixtures/qdisc/" \ - --collector.qdisc.device-include="(wlan0|eth0)" \ - --collector.arp.device-exclude="nope" \ - --no-collector.arp.netlink \ - --collector.hwmon.chip-include="(applesmc|coretemp|hwmon4|nct6779)" \ - --collector.netclass.ignored-devices="(dmz|int)" \ - --collector.netclass.ignore-invalid-speed \ - --collector.netdev.device-include="lo" \ - --collector.bcache.priorityStats \ - "${cpu_info_collector}" \ - --collector.cpu.info.bugs-include="${cpu_info_bugs}" \ - --collector.cpu.info.flags-include="${cpu_info_flags}" \ - --collector.stat.softirq \ - --collector.sysctl.include="kernel.threads-max" \ - --collector.sysctl.include="fs.file-nr" \ - --collector.sysctl.include="fs.file-nr:total,current,max" \ - --collector.sysctl.include-info="kernel.seccomp.actions_avail" \ + $(for c in ${supported_enabled_collectors}; do echo --collector.${c} ; done) \ + $(for c in ${supported_disabled_collectors}; do echo --no-collector.${c} ; done) \ + ${filtered_collector_flags} \ --web.listen-address "127.0.0.1:${port}" \ --log.level="debug" > "${tmpdir}/node_exporter.log" 2>&1 & echo $! > "${tmpdir}/node_exporter.pid" +generated_metrics="${tmpdir}/e2e-output.txt" +for os in freebsd openbsd netbsd solaris dragonfly darwin; do + if [ "${GOHOSTOS}" = "${os}" ]; then + generated_metrics="${tmpdir}/e2e-output-${GOHOSTOS}.txt" + fixture_metrics="${fixture_metrics::-4}-${GOHOSTOS}.txt" + fi +done + finish() { if [ $? -ne 0 -o ${verbose} -ne 0 ] then @@ -165,7 +288,7 @@ EOF if [ ${update} -ne 0 ] then - cp "${tmpdir}/e2e-output.txt" "${fixture}" + cp "${generated_metrics}" "${fixture_metrics}" fi if [ ${keep} -eq 0 ] @@ -195,8 +318,63 @@ get() { sleep 1 -get "127.0.0.1:${port}/metrics" | grep -E -v "${skip_re}" > "${tmpdir}/e2e-output.txt" +get "127.0.0.1:${port}/metrics" | grep --text -E -v "${skip_re}" > "${generated_metrics}" + +# The following ignore-list is only applicable to the VMs used to run E2E tests on platforms for which containerized environments are not available. +# However, owing to this, there are some non-deterministic metrics that end up generating samples, unlike their containerized counterparts, for e.g., node_network_receive_bytes_total. +non_deterministic_metrics=$(cat << METRICS + node_boot_time_seconds + node_cpu_frequency_hertz + node_cpu_seconds_total + node_disk_io_time_seconds_total + node_disk_read_bytes_total + node_disk_read_sectors_total + node_disk_read_time_seconds_total + node_disk_reads_completed_total + node_disk_write_time_seconds_total + node_disk_writes_completed_total + node_disk_written_bytes_total + node_disk_written_sectors_total + node_exec_context_switches_total + node_exec_device_interrupts_total + node_exec_forks_total + node_exec_software_interrupts_total + node_exec_system_calls_total + node_exec_traps_total + node_interrupts_total + node_load1 + node_load15 + node_load5 + node_memory_active_bytes + node_memory_buffer_bytes + node_memory_cache_bytes + node_memory_compressed_bytes + node_memory_free_bytes + node_memory_inactive_bytes + node_memory_internal_bytes + node_memory_laundry_bytes + node_memory_purgeable_bytes + node_memory_size_bytes + node_memory_swapped_in_bytes_total + node_memory_swapped_out_bytes_total + node_memory_wired_bytes + node_netstat_tcp_receive_packets_total + node_netstat_tcp_transmit_packets_total + node_network_receive_bytes_total + node_network_receive_multicast_total + node_network_transmit_multicast_total +METRICS +) + +# Remove non-deterministic metrics from the generated metrics file (as we run their workflows in VMs). +for os in freebsd openbsd netbsd solaris dragonfly darwin; do + if [ "${GOHOSTOS}" = "${os}" ]; then + for metric in ${non_deterministic_metrics}; do + sed -i "/${metric}/d" "${generated_metrics}" + done + fi +done diff -u \ - "${fixture}" \ - "${tmpdir}/e2e-output.txt" + "${fixture_metrics}" \ + "${generated_metrics}" 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 4c1a572b..25434a76 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/prometheus/node_exporter -go 1.21 +go 1.22.0 require ( github.com/alecthomas/kingpin/v2 v2.4.0 @@ -8,53 +8,56 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 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.2 + 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.2.0 - github.com/opencontainers/selinux v1.11.0 + github.com/mdlayher/wifi v0.3.1 + github.com/opencontainers/selinux v1.11.1 + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 github.com/prometheus-community/go-runit v0.1.0 - github.com/prometheus/client_golang v1.19.1 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.55.0 - github.com/prometheus/exporter-toolkit v0.11.0 - github.com/prometheus/procfs v0.15.1 - github.com/safchain/ethtool v0.4.1 - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f - golang.org/x/sys v0.22.0 + github.com/prometheus/common v0.62.0 + github.com/prometheus/exporter-toolkit v0.13.2 + github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b // == v0.15.1 + https://github.com/prometheus/procfs/commit/1754b780536bb81082baa913e04cc4fff4d2baea + github.com/safchain/ethtool v0.5.9 + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 + golang.org/x/sys v0.29.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/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.24.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +//replace github.com/prometheus/procfs => github.com/rexagod/procfs v0.0.0-20241124020414-857c5b813f1b diff --git a/go.sum b/go.sum index bde5ad21..eb4bf236 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ 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= @@ -21,10 +21,6 @@ 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= @@ -41,81 +37,90 @@ 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.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90= -github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM= +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.2.0 h1:vwbVyu5MWTiFNvOmWdvIx9veBlMVnEasZ90PhUi1DYU= -github.com/mdlayher/wifi v0.2.0/go.mod h1:yOfWhVZ4FFJxeHzAxDzt87Om9EkqqcCiY9Gi5gfSXwI= +github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/mdlayher/wifi v0.3.1 h1:bZDuMI1f7z5BtUUO3NgHRdR/R88YtywIe6dsEFI0Txs= +github.com/mdlayher/wifi v0.3.1/go.mod h1:ODQaObvsglghTuNhezD9grkTB4shVNc28aJfTXmvSi8= github.com/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/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8= +github.com/opencontainers/selinux v1.11.1/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.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/exporter-toolkit v0.13.2 h1:Z02fYtbqTMy2i/f+xZ+UK5jy/bl1Ex3ndzh06T/Q9DQ= +github.com/prometheus/exporter-toolkit v0.13.2/go.mod h1:tCqnfx21q6qN1KA4U3Bfb8uWzXfijIrJz3/kTIqMV7g= +github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b h1:4EJkx3vycI+n5JY5ht+bnSUGamkmmXkpcNeO/OBT/0A= +github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/safchain/ethtool v0.4.1 h1:S6mEleTADqgynileXoiapt/nKnatyR6bmIHoF+h2ADo= -github.com/safchain/ethtool v0.4.1/go.mod h1:XLLnZmy4OCRTkksP/UiMjij96YmIsBfmBQcs7H6tA48= +github.com/safchain/ethtool v0.5.9 h1://6RvaOKFf3nQ0rl5+8zBbE4/72455VC9Jq61pfq67E= +github.com/safchain/ethtool v0.5.9/go.mod h1:w8oSsZeowyRaM7xJJBAbubzzrOkwO8TBgPSEqPP/5mg= 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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.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.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/node_exporter.go b/node_exporter.go index c7285f6f..88441e1e 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -15,20 +15,19 @@ 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" @@ -44,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, @@ -75,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 @@ -108,14 +132,13 @@ 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) } } @@ -130,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, @@ -145,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, }, @@ -179,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 != "/" { @@ -213,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) @@ -221,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/tools/main.go b/tools/main.go new file mode 100644 index 00000000..cfd4c113 --- /dev/null +++ b/tools/main.go @@ -0,0 +1,79 @@ +// 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 main + +import ( + "flag" + "fmt" + "go/build" + "os" + "path/filepath" + "runtime" +) + +func main() { + printHelpAndDie := func() { + fmt.Println(` +Usage: tools [command]`) + os.Exit(1) + } + if len(os.Args) < 2 { + printHelpAndDie() + } + + // Sub-commands. + matchCmd := flag.NewFlagSet("match", flag.ExitOnError) + switch os.Args[1] { + case "match": + err := matchCmd.Parse(os.Args[2:]) + if err != nil { + fmt.Println("Error parsing flags:", err) + os.Exit(1) + } + if matchCmd.NArg() != 1 { + fmt.Println("Usage: match [file]") + os.Exit(1) + } + file := matchCmd.Arg(0) + + // For debugging purposes, allow overriding these. + goos, found := os.LookupEnv("GOHOSTOS") + if !found { + goos = runtime.GOOS + } + goarch, found := os.LookupEnv("GOARCH") + if !found { + goarch = runtime.GOARCH + } + ctx := build.Context{ + GOOS: goos, + GOARCH: goarch, + } + abs, err := filepath.Abs(file) + if err != nil { + panic(err) + } + match, err := ctx.MatchFile(filepath.Dir(abs), filepath.Base(abs)) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + if match { + os.Exit(0) + } + os.Exit(1) + default: + printHelpAndDie() + } +}