Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ qdisc | Exposes [queuing discipline](https://en.wikipedia.org/wiki/Network_sched
slabinfo | Exposes slab statistics from `/proc/slabinfo`. Note that permission of `/proc/slabinfo` is usually 0400, so set it appropriately. | Linux
softirqs | Exposes detailed softirq statistics from `/proc/softirqs`. | Linux
sysctl | Expose sysctl values from `/proc/sys`. Use `--collector.sysctl.include(-info)` to configure. | Linux
swap | Expose swap information from `/proc/swaps`. | Linux
systemd | Exposes service and system status from [systemd](http://www.freedesktop.org/wiki/Software/systemd/). | Linux
tcpstat | Exposes TCP connection status information from `/proc/net/tcp` and `/proc/net/tcp6`. (Warning: the current version has potential performance issues in high load situations.) | Linux
wifi | Exposes WiFi device and station statistics. | Linux
Expand Down
2 changes: 2 additions & 0 deletions collector/fixtures/proc/swaps
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Filename Type Size Used Priority
/dev/zram0 partition 8388604 76 100
129 changes: 129 additions & 0 deletions collector/swap_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 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 !noswap

package collector

import (
"fmt"
"log/slog"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
)

const (
swapSubsystem = "swap"
)

var swapLabelNames = []string{"device", "swap_type"}

type swapCollector struct {
fs procfs.FS
logger *slog.Logger
}

func init() {
registerCollector("swap", defaultDisabled, NewSwapCollector)
}

// NewSwapCollector returns a new Collector exposing swap device statistics.
func NewSwapCollector(logger *slog.Logger) (Collector, error) {
fs, err := procfs.NewFS(*procPath)
if err != nil {
return nil, fmt.Errorf("failed to open procfs: %w", err)
}

return &swapCollector{
fs: fs,
logger: logger,
}, nil
}

type SwapsEntry struct {
Device string
Type string
Priority int
Size int
Used int
}

func (c *swapCollector) getSwapInfo() ([]SwapsEntry, error) {
swaps, err := c.fs.Swaps()
if err != nil {
return nil, fmt.Errorf("couldn't get proc/swap information: %w", err)
}

metrics := make([]SwapsEntry, 0, len(swaps))

for _, swap := range swaps {
metrics = append(metrics, SwapsEntry{Device: swap.Filename, Type: swap.Type,
Priority: swap.Priority, Size: swap.Size, Used: swap.Used})
}

return metrics, nil
}

func (c *swapCollector) Update(ch chan<- prometheus.Metric) error {
swaps, err := c.getSwapInfo()
if err != nil {
return fmt.Errorf("couldn't get swap information: %w", err)
}

for _, swap := range swaps {
swapLabelValues := []string{swap.Device, swap.Type}

// Export swap size in bytes
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
prometheus.BuildFQName(namespace, swapSubsystem, "size_bytes"),
"Swap device size in bytes.",
[]string{"device", "swap_type"}, nil,
),
prometheus.GaugeValue,
// Size is provided in kbytes (not bytes), translate to bytes
// see https://github.com/torvalds/linux/blob/fd94619c43360eb44d28bd3ef326a4f85c600a07/mm/swapfile.c#L3079-L3080
float64(swap.Size*1024),
swapLabelValues...,
)

// Export swap used in bytes
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
prometheus.BuildFQName(namespace, swapSubsystem, "used_bytes"),
"Swap device used in bytes.",
swapLabelNames, nil,
),
prometheus.GaugeValue,
// Swap used is also provided in kbytes, translate to bytes
float64(swap.Used*1024),
swapLabelValues...,
)

// Export swap priority
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
prometheus.BuildFQName(namespace, swapSubsystem, "priority"),
"Swap device priority.",
swapLabelNames, nil,
),
prometheus.GaugeValue,
float64(swap.Priority),
swapLabelValues...,
)

}

return nil
}
58 changes: 58 additions & 0 deletions collector/swap_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 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 !noswap
// +build !noswap

package collector

import (
"io"
"log/slog"
"testing"
)

func TestSwap(t *testing.T) {
*procPath = "fixtures/proc"
logger := slog.New(slog.NewTextHandler(io.Discard, nil))

collector, err := NewSwapCollector(logger)
if err != nil {
panic(err)
}

swapInfo, err := collector.(*swapCollector).getSwapInfo()
if err != nil {
panic(err)
}

if want, got := "/dev/zram0", swapInfo[0].Device; want != got {
t.Errorf("want swap device %s, got %s", want, got)
}

if want, got := "partition", swapInfo[0].Type; want != got {
t.Errorf("want swap type %s, got %s", want, got)
}

if want, got := 100, swapInfo[0].Priority; want != got {
t.Errorf("want swap priority %d, got %d", want, got)
}

if want, got := 8388604, swapInfo[0].Size; want != got {
t.Errorf("want swap size %d, got %d", want, got)
}

if want, got := 76, swapInfo[0].Used; want != got {
t.Errorf("want swpa used %d, got %d", want, got)
}
}
Loading