Help us shape the future of Go! Take the 2026 Go Developer Survey

metrics

package module
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: BSD-3-Clause Imports: 17 Imported by: 0

README

sysc-metrics

Read-only Linux system telemetry for Go. CPU, memory, disks, network, temperature, battery, GPU and processes, read straight from /proc and /sys with nothing but the standard library. It feeds the bar widgets and system monitor in sysc-shell.

The sysc-shell system monitor, drawn from sysc-metrics data
The sysc-shell system monitor, fed by sysc-metrics (fixture data).

Documentation site · Features · Usage · Development

Features

  • CPU: total and per-core usage, per-core frequency, and load averages
  • Memory: RAM and swap
  • Disks: capacity for every mounted filesystem, plus read/write rates and busy time per block device
  • Network: byte and packet rates per interface, with error and drop counters
  • Temperature: one CPU package reading, from k10temp, coretemp or a thermal zone, whichever the machine has
  • Battery: charge, state, power draw and time remaining, combined across every battery and UPS
  • GPU: usage, temperature, name and VRAM for AMD, NVIDIA and Intel
  • Processes: CPU and memory per process, with identities that don't get confused by PID reuse
  • Uptime
  • Small: Linux only, standard library only, and it never starts a goroutine

Installation

Requires: Go 1.26+ on Linux.

go get github.com/Nomadcxx/sysc-metrics

Usage

Values that don't depend on time come from a single read:

mem, err := metrics.ReadMemory()
if err != nil {
	return err
}
fmt.Printf("memory: %d of %d bytes used\n", mem.Memory.UsedBytes, mem.Memory.TotalBytes)

ReadMemory, ReadFilesystems, ReadThermal, ReadBattery, ReadUptime and ReadGPU all work this way.

Rates need two readings, so CPU, disk, network, process and GPU usage come from a sampler. Call Sample on your own ticker. The first sample only sets a baseline, and its rates come back with Valid == false:

cpu := metrics.NewCPUSampler()
gpu := metrics.NewGPUSampler()
defer gpu.Close()

for range time.Tick(time.Second) {
	c, err := cpu.Sample()
	if err != nil {
		return err
	}
	if c.Usage.Valid {
		fmt.Printf("cpu: %.0f%%\n", c.Usage.Fraction*100)
	}

	g, _ := gpu.Sample()
	for _, card := range g.GPUs {
		if card.Usage.Valid {
			fmt.Printf("%s: %.0f%%\n", card.Name, card.Usage.Fraction*100)
		}
	}
}

On a desktop with an RTX 4060 that prints:

memory: 18722406400 of 33559834624 bytes used
AD106 [GeForce RTX 4060]: 6%
cpu: 14%
AD106 [GeForce RTX 4060]: 5%
cpu: 12%

A few rules hold everywhere:

  • Check Valid. It separates a real zero from "no reading". An idle GPU at 0% is valid; a GPU nobody can measure is not.
  • Snapshots can be partial. One unreadable mount or sensor doesn't fail the whole read. It shows up in the snapshot's Issues, and everything else is filled in.
  • A missing battery or sensor is not an error. ReadBattery reports Present == false on a desktop, and ReadThermal reports Valid == false where there is no known sensor.
  • One sampler, one caller. Samplers keep the previous reading and aren't safe for concurrent use. Give each polling loop its own. Close a GPUSampler when you stop polling, because it holds open counters.

How often to poll, what to cache, and how to show the numbers is up to you.

GPU usage

Each vendor exposes usage differently:

GPU Usage VRAM
AMD gpu_busy_percent in sysfs mem_info_vram_used / mem_info_vram_total
NVIDIA nvidia-smi, run only when an NVIDIA card is present same nvidia-smi query
Intel GPUSampler only, from the second sample on not reported

ReadGPU never reports Intel usage. GPUSampler gets it from the i915 PMU counters, which need CAP_PERFMON on the process. Without that, it falls back to the per-client DRM counters in /proc/*/fdinfo, which covers i915 and amdgpu. That fallback only sees your own processes, so if the only GPU clients belong to another user (a greeter, say), usage stays invalid rather than showing a false zero. The newer Intel xe driver isn't supported yet.

To grant CAP_PERFMON to a program that uses this library, see Intel GPU usage in sysc-shell.

What it doesn't do

No power controls, directory sizes, file indexing, SMART data, quotas, vendor tools beyond nvidia-smi, daemon, or other operating systems.

Development

go vet ./...
go test -race -count=1 ./...

On a machine with an Intel i915 GPU, check the live PMU path with:

SYSC_METRICS_INTEL_GPU_LIVE=1 go test -run TestIntelGPULive .

License

BSD-3-Clause


RAMA — terminal-native tooling for the linux desktop. More projects → · Sponsor ❤️

Documentation

Overview

Package metrics provides read-only Linux system telemetry snapshots.

Index

Constants

This section is empty.

Variables

View Source
var ErrProcessIdentityChanged = errors.New("process identity changed")

ErrProcessIdentityChanged means a PID now identifies a different process.

Functions

func ValidateProcessIdentity added in v0.4.0

func ValidateProcessIdentity(identity ProcessIdentity) error

ValidateProcessIdentity confirms that identity still names the same process.

Types

type BatterySnapshot added in v0.2.0

type BatterySnapshot struct {
	CollectedAt   time.Time
	Present       bool
	Charge        float64 // 0..1
	ChargeValid   bool
	State         BatteryState
	EnergyJoules  float64
	RateWatts     float64
	RateValid     bool
	TimeRemaining time.Duration
	TimeValid     bool
	Issues        []Issue
}

BatterySnapshot is the aggregate battery observation. Present is false on a machine with no battery; that is not an error.

func ReadBattery added in v0.2.0

func ReadBattery() (BatterySnapshot, error)

ReadBattery collects the current aggregate battery. It prefers sysfs power-supply devices of type Battery or UPS. A machine with none reports Present false.

type BatteryState added in v0.2.0

type BatteryState uint8

BatteryState is the charging state of the aggregate battery.

const (
	BatteryUnknown BatteryState = iota
	BatteryCharging
	BatteryDischarging
	BatteryFull
)

type BlockDevice

type BlockDevice struct {
	Name            string
	Major           uint32
	Minor           uint32
	ReadBytes       uint64
	WriteBytes      uint64
	ReadOperations  uint64
	WriteOperations uint64
	Busy            time.Duration
	Rates           BlockRates
}

BlockDevice contains cumulative counters and derived rates for one block device.

type BlockRates

type BlockRates struct {
	ReadBytesPerSecond       float64
	WriteBytesPerSecond      float64
	ReadOperationsPerSecond  float64
	WriteOperationsPerSecond float64
	BusyFraction             float64
	Valid                    bool
}

BlockRates contains rates derived from two block-device observations. Valid distinguishes observed zero rates from unavailable derived values.

type BlockSampler

type BlockSampler struct {
	// contains filtered or unexported fields
}

BlockSampler retains previous block counters for sequential rate sampling.

func NewBlockSampler

func NewBlockSampler() *BlockSampler

NewBlockSampler returns a block sampler owned by one sequential polling caller.

func (*BlockSampler) Sample

func (s *BlockSampler) Sample() (BlockSnapshot, error)

Sample returns one block-device snapshot. The sampler is not safe for concurrent use.

type BlockSnapshot

type BlockSnapshot struct {
	CollectedAt time.Time
	Devices     []BlockDevice
	Issues      []Issue
}

BlockSnapshot contains block-device data collected at one time. Devices and Issues are caller-owned snapshot slices.

type CPUCore

type CPUCore struct {
	ID             int
	Usage          CPUUsage
	FrequencyHz    uint64
	FrequencyValid bool
}

CPUCore contains one core's usage and best-effort frequency.

type CPUSampler

type CPUSampler struct {
	// contains filtered or unexported fields
}

CPUSampler retains the previous CPU counters for sequential rate sampling.

func NewCPUSampler

func NewCPUSampler() *CPUSampler

NewCPUSampler returns a CPU sampler owned by one sequential polling caller.

func (*CPUSampler) Sample

func (s *CPUSampler) Sample() (CPUSnapshot, error)

Sample returns one CPU snapshot. The sampler is not safe for concurrent use.

type CPUSnapshot

type CPUSnapshot struct {
	CollectedAt time.Time
	Usage       CPUUsage
	Cores       []CPUCore
	Load1       float64
	Load5       float64
	Load15      float64
	LoadValid   bool
	Issues      []Issue
}

CPUSnapshot contains aggregate and per-core CPU data collected at one time. Cores and Issues are caller-owned snapshot slices.

type CPUUsage

type CPUUsage struct {
	Fraction float64
	Valid    bool
}

CPUUsage contains a utilization fraction. Valid distinguishes an observed zero from no sample.

type Capacity

type Capacity struct {
	TotalBytes     uint64
	UsedBytes      uint64
	AvailableBytes uint64
}

Capacity contains byte totals. Byte fields are uint64 values.

type Filesystem

type Filesystem struct {
	MountID    uint64
	MountPoint string
	Source     string
	Type       string
	ReadOnly   bool
	Capacity   Capacity
}

Filesystem contains one mounted filesystem and its byte capacity.

type FilesystemSnapshot

type FilesystemSnapshot struct {
	CollectedAt time.Time
	Filesystems []Filesystem
	Issues      []Issue
}

FilesystemSnapshot contains mounted filesystem data collected at one time. Filesystems and Issues are caller-owned snapshot slices.

func ReadFilesystems

func ReadFilesystems() (FilesystemSnapshot, error)

ReadFilesystems returns one mounted-filesystem snapshot from Linux. Autofs trigger points are not reported: statfs on a trigger fires the automount, and anything mounted on top has its own mountinfo row.

type GPU added in v0.3.0

type GPU struct {
	PCIID     string
	Driver    string
	Name      string
	Usage     GPUUsage
	Celsius   float64
	TempValid bool
	// VRAM is dedicated device memory. VRAMValid is false when the driver
	// does not report both used and total; an integrated part's shared
	// system memory is never counted here.
	VRAM      Capacity
	VRAMValid bool
}

GPU is one display device.

type GPUSampler added in v0.5.0

type GPUSampler struct {
	// contains filtered or unexported fields
}

GPUSampler retains Intel i915 PMU engine counters and DRM client fdinfo baselines for sequential rate sampling. It is owned by one sequential polling caller, starts no goroutines, and owns open PMU counters until Close. ReadGPU cannot report Intel usage; a consumer that needs it must sample here and close the sampler when its polling stops.

Without the privilege the PMU needs, usage for a GPU still lacking it comes from walking /proc for this user's /dev/dri descriptors: one readlink per descriptor each sample, and only while some GPU lacks usage. Other users' clients are invisible to that walk, so usage can stay invalid.

func NewGPUSampler added in v0.5.0

func NewGPUSampler() *GPUSampler

NewGPUSampler returns a GPU sampler owned by one sequential polling caller. It reports the same snapshot as ReadGPU and additionally fills Intel i915 usage, and usage for any GPU the other paths leave empty, from the second sample on (PMU counters, else DRM client fdinfo).

func (*GPUSampler) Close added in v0.5.0

func (s *GPUSampler) Close() error

Close closes every open PMU counter and drops sampling state. It is idempotent; sampling after Close starts from a fresh baseline.

func (*GPUSampler) Sample added in v0.5.0

func (s *GPUSampler) Sample() (GPUSnapshot, error)

Sample returns one GPU snapshot. The sampler is not safe for concurrent use.

type GPUSnapshot added in v0.3.0

type GPUSnapshot struct {
	CollectedAt time.Time
	GPUs        []GPU
	Issues      []Issue
}

GPUSnapshot lists GPUs collected at one time. GPUs and Issues are caller-owned.

func ReadGPU added in v0.3.0

func ReadGPU() (GPUSnapshot, error)

ReadGPU collects usage and temperature for each discrete or integrated GPU.

type GPUUsage added in v0.3.0

type GPUUsage struct {
	Fraction float64
	Valid    bool
}

GPUUsage is a 0..1 busy fraction. Valid distinguishes a real zero from no sample.

type Issue

type Issue struct {
	Source string
	Err    error
}

Issue describes a recoverable source or entity failure in a partial snapshot.

func (Issue) Error

func (i Issue) Error() string

Error returns the source and underlying error.

func (Issue) Unwrap

func (i Issue) Unwrap() error

Unwrap returns the underlying source error.

type MemorySnapshot

type MemorySnapshot struct {
	CollectedAt time.Time
	Memory      Capacity
	Swap        Capacity
}

MemorySnapshot contains memory and swap byte capacities collected at one time.

func ReadMemory

func ReadMemory() (MemorySnapshot, error)

ReadMemory returns one memory and swap snapshot from Linux.

type NetworkInterface

type NetworkInterface struct {
	Name            string
	Index           uint32
	ReceiveBytes    uint64
	ReceivePackets  uint64
	ReceiveErrors   uint64
	ReceiveDropped  uint64
	TransmitBytes   uint64
	TransmitPackets uint64
	TransmitErrors  uint64
	TransmitDropped uint64
	Rates           NetworkRates
}

NetworkInterface contains cumulative counters and derived rates for one interface.

type NetworkRates

type NetworkRates struct {
	ReceiveBytesPerSecond    float64
	TransmitBytesPerSecond   float64
	ReceivePacketsPerSecond  float64
	TransmitPacketsPerSecond float64
	Valid                    bool
}

NetworkRates contains rates derived from two network observations. Valid distinguishes observed zero rates from unavailable derived values.

type NetworkSampler

type NetworkSampler struct {
	// contains filtered or unexported fields
}

NetworkSampler retains previous interface counters for sequential rate sampling.

func NewNetworkSampler

func NewNetworkSampler() *NetworkSampler

NewNetworkSampler returns a network sampler owned by one sequential polling caller.

func (*NetworkSampler) Sample

func (s *NetworkSampler) Sample() (NetworkSnapshot, error)

Sample returns one network-interface snapshot. The sampler is not safe for concurrent use.

type NetworkSnapshot

type NetworkSnapshot struct {
	CollectedAt time.Time
	Interfaces  []NetworkInterface
	Issues      []Issue
}

NetworkSnapshot contains network-interface data collected at one time. Interfaces and Issues are caller-owned snapshot slices.

type Process added in v0.4.0

type Process struct {
	Identity      ProcessIdentity
	Name          string
	ParentPID     int
	UID           uint32
	UIDValid      bool
	ResidentBytes uint64
	ResidentValid bool
	Args          []string
	CPU           CPUUsage
}

Process contains one process observation and its best-effort derived CPU usage.

type ProcessIdentity added in v0.4.0

type ProcessIdentity struct {
	PID            int
	StartTimeTicks uint64
}

ProcessIdentity distinguishes a process from a later process that reuses its PID.

type ProcessSampler added in v0.4.0

type ProcessSampler struct {
	// contains filtered or unexported fields
}

ProcessSampler retains previous process and aggregate CPU counters for sequential rate sampling. previousTotal is the aggregate tick count read before the previous process walk. The next sample closes that window with a second read taken after its own walk, so both process stats fall inside it.

func NewProcessSampler added in v0.4.0

func NewProcessSampler() *ProcessSampler

NewProcessSampler returns a process sampler owned by one sequential polling caller.

func (*ProcessSampler) Sample added in v0.4.0

func (s *ProcessSampler) Sample() (ProcessSnapshot, error)

Sample returns one process snapshot. The sampler is not safe for concurrent use.

Aggregate ticks are read before the walk and again after every process stat. The window for this sample runs from the previous pre-walk read to this post-walk read, which covers both process observations. A saturated process read later than the opening /proc/stat therefore stays inside the fraction instead of being dropped (issue #21). A delta that still exceeds that covering window is a counter regression and stays invalid (issue #7).

type ProcessSnapshot added in v0.4.0

type ProcessSnapshot struct {
	CollectedAt time.Time
	Processes   []Process
	Issues      []Issue
}

ProcessSnapshot contains processes collected at one time. Processes, Args, and Issues are caller-owned snapshot slices.

type ThermalSnapshot added in v0.3.0

type ThermalSnapshot struct {
	CollectedAt time.Time
	Celsius     float64
	Valid       bool
	Source      string
	Issues      []Issue
}

ThermalSnapshot is one scored CPU package temperature in Celsius. Valid is false when no known sensor exists; that is not an error.

func ReadThermal added in v0.3.0

func ReadThermal() (ThermalSnapshot, error)

ReadThermal collects the current CPU package temperature.

type UptimeSnapshot

type UptimeSnapshot struct {
	CollectedAt time.Time
	Uptime      time.Duration
}

UptimeSnapshot contains uptime as a duration collected at one time.

func ReadUptime

func ReadUptime() (UptimeSnapshot, error)

ReadUptime returns one uptime snapshot from Linux.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL