DEV Community

Cover image for Gopsutil: Powerful System Stats for Go Developers
Leapcell
Leapcell

Posted on

1 2 1 1 1

Gopsutil: Powerful System Stats for Go Developers

Image description

Leapcell: The Next-Gen Serverless Platform for Web Hosting

Introduction

gopsutil is a Golang port of the Python library psutil, which helps us conveniently obtain various system and hardware information. It masks the differences between different systems and has extremely powerful portability. With gopsutil, we don't need to use syscall to call the corresponding system methods for different systems. What's even better is that the implementation of gopsutil does not contain any cgo code, which makes cross-compilation possible.

Quick Start

Installation

Execute the following command to install:

$ go get github.com/shirou/gopsutil
Enter fullscreen mode Exit fullscreen mode

Usage Example

package main

import (
    "fmt"
    "github.com/shirou/gopsutil/mem"
)

func main() {
    v, _ := mem.VirtualMemory()
    fmt.Printf("Total: %v, Available: %v, UsedPercent:%f%%\n", v.Total, v.Available, v.UsedPercent)
    fmt.Println(v)
}
Enter fullscreen mode Exit fullscreen mode

gopsutil divides different functions into different sub-packages:

  • cpu: Related to the CPU;
  • disk: Related to the disk;
  • docker: Related to docker;
  • host: Related to the host;
  • mem: Related to the memory;
  • net: Related to the network;
  • process: Related to the process;
  • macservices: Related to Mac services (the original winservices corresponds to Windows services, which is modified here for the Mac system).

To use the corresponding functions, you need to import the corresponding sub-packages. For example, in the above code, to obtain memory information, the mem sub-package is imported. The mem.VirtualMemory() method returns the memory information structure mem.VirtualMemoryStat, which contains rich fields. The main ones we commonly use are Total (total memory), Available (available memory), Used (used memory), and UsedPercent (memory usage percentage). mem.VirtualMemoryStat also implements the fmt.Stringer interface and returns the memory information in JSON format. The statement fmt.Println(v) will automatically call v.String() to output the returned information. Suppose the program outputs the following (the data here is a reasonable assumed value):

Total: 16441110528, Available: 8589934592, UsedPercent:47.730000%
{"total":16441110528,"available":8589934592,"used":7851175936,"usedPercent":47.730000,"free":7959955456,"active":8220555264,"inactive":6815744000,"wired":1429770240,"laundry":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"sreclaimable":0,"sunreclaim":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0,"hightotal":0,"highfree":0,"lowtotal":0,"lowfree":0,"swaptotal":0,"swapfree":0,"mapped":0,"vmalloctotal":0,"vmallocused":0,"vmallocchunk":0,"hugepagestotal":0,"hugepagesfree":0,"hugepagesize":0}
Enter fullscreen mode Exit fullscreen mode

The unit is bytes. Suppose the computer memory is 16GB, the current usage percentage is 47.73%, and the available memory is 8589934592B (i.e., 8GB).

CPU

The number of CPU cores is divided into the number of physical cores and the number of logical cores. The number of physical cores is the actual number of CPUs on the motherboard. There may be multiple cores on a physical CPU, and these cores are called logical cores. The functions related to the CPU in gopsutil are located in the cpu sub-package. This sub-package provides interfaces to obtain the number of physical and logical cores and the CPU usage:

  • Counts(logical bool): Pass in false to return the number of physical cores; pass in true to return the number of logical cores;
  • Percent(interval time.Duration, percpu bool): Used to obtain the CPU usage within the interval time interval. When percpu is false, obtain the total CPU usage; when percpu is true, obtain the usage of each CPU respectively and return a value of type []float64.

For example:

func main() {
    physicalCnt, _ := cpu.Counts(false)
    logicalCnt, _ := cpu.Counts(true)
    fmt.Printf("physical count:%d logical count:%d\n", physicalCnt, logicalCnt)

    totalPercent, _ := cpu.Percent(3*time.Second, false)
    perPercents, _ := cpu.Percent(3*time.Second, true)
    fmt.Printf("total percent:%v per percents:%v", totalPercent, perPercents)
}
Enter fullscreen mode Exit fullscreen mode

The above code obtains the number of physical and logical cores and the total CPU usage and the usage of each CPU within 3 seconds. The program output (the output may be different each time it runs, and the values here are assumed):

physical count:12 logical count:12
total percent:[6.59835041239871] per percents:[15.77181208051725 14.04682274248692 11.03678929768094 7.692307692328751 3.6789297658885762 1.999999999998181 0.664451827243077 0 0 0 0 0]
Enter fullscreen mode Exit fullscreen mode

Detailed Information

Calling cpu.Info() can obtain detailed information about the CPU and return []cpu.InfoStat:

func main() {
    infos, _ := cpu.Info()
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", " ")
        fmt.Print(string(data))
    }
}
Enter fullscreen mode Exit fullscreen mode

For ease of viewing, the results are output in JSON format:

{
    "cpu": 0,
    "vendorId": "Apple",
    "family": "Apple Silicon",
    "model": "M1 Pro",
    "stepping": 0,
    "physicalId": "abcd1234",
    "coreId": "",
    "cores": 10,
    "modelName": "Apple M1 Pro",
    "mhz": 3200,
    "cacheSize": 32768,
    "flags": [],
    "microcode": ""
}
Enter fullscreen mode Exit fullscreen mode

From the results, we can see that the CPU is from Apple's M1 Pro series with a frequency of 3.2GHz. This is the returned result when running on a Mac, and the github.com/StackExchange/wmi library is used internally (assuming that this library still has relevant functions in the Mac scenario, and it may actually need to be adjusted). Under Linux, each logical CPU will return an InfoStat structure.

Time Occupancy

Calling cpu.Times(percpu bool) can obtain the time occupancy of the total CPU and each individual CPU from the boot time. Pass in percpu = false to return the total value, and pass in percpu = true to return the value for each individual CPU. The time occupancy of each CPU is represented by a TimeStat structure:

// src/github.com/shirou/gopsutil/cpu/cpu.go
type TimesStat struct {
    CPU       string  `json:"cpu"`
    User      float64 `json:"user"`
    System    float64 `json:"system"`
    Idle      float64 `json:"idle"`
    Nice      float64 `json:"nice"`
    Iowait    float64 `json:"iowait"`
    Irq       float64 `json:"irq"`
    Softirq   float64 `json:"softirq"`
    Steal     float64 `json:"steal"`
    Guest     float64 `json:"guest"`
    GuestNice float64 `json:"guestNice"`
}
Enter fullscreen mode Exit fullscreen mode
  • CPU: CPU identifier. If it is the total value, this field is cpu - total, otherwise it is cpu0, cpu1, etc.;
  • User: User time occupancy (user mode);
  • System: System time occupancy (kernel mode);
  • Idle: Idle time;
  • ……

For example:

func main() {
    infos, _ := cpu.Times(true)
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", " ")
        fmt.Print(string(data))
    }
}
Enter fullscreen mode Exit fullscreen mode

For ease of viewing, the results are output in JSON format. The following is one of the outputs (assumed value):

{
    "cpu": "cpu0",
    "user": 123.45,
    "system": 234.56,
    "idle": 789.12,
    "nice": 0,
    "iowait": 0,
    "irq": 0,
    "softirq": 0,
    "steal": 0,
    "guest": 0,
    "guestNice": 0
}
Enter fullscreen mode Exit fullscreen mode

Disk

The sub-package disk is used to obtain disk information, and it can obtain IO statistics, partition, and usage information. The following is an introduction one by one.

IO Statistics

Call the disk.IOCounters() function, and the returned IO statistics information is represented by the type map[string]IOCountersStat. Each partition corresponds to a structure, with the key being the partition name and the value being the statistical information. Here, some fields of the statistical structure are selected, mainly including the number of reads and writes, the number of bytes, and the time:

// src/github.com/shirou/gopsutil/disk/disk.go
type IOCountersStat struct {
    ReadCount        uint64 `json:"readCount"`
    MergedReadCount  uint64 `json:"mergedReadCount"`
    WriteCount       uint64 `json:"writeCount"`
    MergedWriteCount uint64 `json:"mergedWriteCount"`
    ReadBytes        uint64 `json:"readBytes"`
    WriteBytes       uint64 `json:"writeBytes"`
    ReadTime         uint64 `json:"readTime"`
    WriteTime        uint64 `json:"writeTime"`
    // ……
}
Enter fullscreen mode Exit fullscreen mode

For example:

func main() {
    mapStat, _ := disk.IOCounters()
    for name, stat := range mapStat {
        fmt.Println(name)
        data, _ := json.MarshalIndent(stat, "", "  ")
        fmt.Println(string(data))
    }
}
Enter fullscreen mode Exit fullscreen mode

The output contains all partitions, and only one is shown here (assumed value):

disk0s2
{
  "readCount": 123456,
  "mergedReadCount": 0,
  "writeCount": 789012,
  "mergedWriteCount": 0,
  "readBytes": 5678901234,
  "writeBytes": 9876543210,
  "readTime": 200,
  "writeTime": 300,
  "iopsInProgress": 0,
  "ioTime": 0,
  "weightedIO": 0,
  "name": "disk0s2",
  "serialNumber": "1234567890ABCDEF",
  "label": "Macintosh HD"
}
Enter fullscreen mode Exit fullscreen mode

Note that disk.IOCounters() can accept a variable number of string parameters to identify partitions, and this parameter is invalid on Mac (the original Windows-related description has been adjusted).

Partition

Call the disk.PartitionStat(all bool) function to return partition information. If all = false, only the actual physical partitions (including hard disks, CD-ROMs, USBs) are returned, and other virtual partitions are ignored; if all = true, all partitions are returned. The return type is []PartitionStat, and each partition corresponds to a PartitionStat structure:

// src/github.com/shirou/gopsutil/disk/
type PartitionStat struct {
    Device     string `json:"device"`
    Mountpoint string `json:"mountpoint"`
    Fstype     string `json:"fstype"`
    Opts       string `json:"opts"`
}
Enter fullscreen mode Exit fullscreen mode
  • Device: Partition identifier. On Mac, for example, it is in the format of disk0s2;
  • Mountpoint: Mount point, that is, the starting position of the file path of this partition;
  • Fstype: File system type. Commonly used file system types on Mac include APFS, etc.;
  • Opts: Options, which are related to the system.

For example:

func main() {
    infos, _ := disk.Partitions(false)
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", "  ")
        fmt.Println(string(data))
    }
}
Enter fullscreen mode Exit fullscreen mode

The output on a Mac machine (only the first partition is shown, assumed value):

{
    "device": "disk0s2",
    "mountpoint": "/",
    "fstype": "APFS",
    "opts": "rw"
}
Enter fullscreen mode Exit fullscreen mode

From the above output, we can see that the first partition is disk0s2 and the file system type is APFS.

Usage

Calling disk.Usage(path string) can obtain the usage of the disk where the path path is located and return a UsageStat structure:

// src/github.com/shirou/gopsutil/disk.go
type UsageStat struct {
    Path              string  `json:"path"`
    Fstype            string  `json:"fstype"`
    Total             uint64  `json:"total"`
    Free              uint64  `json:"free"`
    Used              uint64  `json:"used"`
    UsedPercent       float64 `json:"usedPercent"`
    InodesTotal       uint64  `json:"inodesTotal"`
    InodesUsed        uint64  `json:"inodesUsed"`
    InodesFree        uint64  `json:"inodesFree"`
    InodesUsedPercent float64 `json:"inodesUsedPercent"`
}
Enter fullscreen mode Exit fullscreen mode
  • Path: Path, the passed parameter;
  • Fstype: File system type;
  • Total: Total capacity of this partition;
  • Free: Free capacity;
  • Used: Used capacity;
  • UsedPercent: Usage percentage.

For example:

func main() {
    info, _ := disk.Usage("/Users")
    data, _ := json.MarshalIndent(info, "", "  ")
    fmt.Println(string(data))
}
Enter fullscreen mode Exit fullscreen mode

Since the returned value is the usage of the disk, the paths /Users and the root path of the disk return similar results, only the Path field in the structure is different. The program output (assumed value):

{
    "path": "/Users",
    "fstype": "APFS",
    "total": 499999999999,
    "free": 300000000000,
    "used": 199999999999,
    "usedPercent": 39.99,
    "inodesTotal": 0,
    "inodesUsed": 0,
    "inodesFree": 0,
    "inodesUsedPercent": 0
}
Enter fullscreen mode Exit fullscreen mode

Host

The sub-package host can obtain host-related information, such as boot time, kernel version number, platform information, etc.

Boot Time

host.BootTime() returns the timestamp of the host's boot time:

func main() {
    timestamp, _ := host.BootTime()
    t := time.Unix(int64(timestamp), 0)
    fmt.Println(t.Local().Format("2006-01-02 15:04:05"))
}
Enter fullscreen mode Exit fullscreen mode

The above code first obtains the boot time, then converts it to the time.Time type through time.Unix(), and finally outputs the time in the format of 2006 - 01 - 02 15:04:05 (assumed value):

2025-03-15 16:30:15
Enter fullscreen mode Exit fullscreen mode

Kernel Version and Platform Information

func main() {
    version, _ := host.KernelVersion()
    fmt.Println(version)

    platform, family, version, _ := host.PlatformInformation()
    fmt.Println("platform:", platform)
    fmt.Println("family:", family)
    fmt.Println("version:", version)
}
Enter fullscreen mode Exit fullscreen mode

The output when running on a Mac (assumed value):

22.6.0
platform: macOS 13.5
family: Darwin
version: 22.6.0
Enter fullscreen mode Exit fullscreen mode

Terminal Users

host.Users() returns the information of users connected via the terminal, and each user corresponds to a UserStat structure:

// src/github.com/shirou/gopsutil/host/host.go
type UserStat struct {
    User     string `json:"user"`
    Terminal string `json:"terminal"`
    Host     string `json:"host"`
    Started  int    `json:"started"`
}
Enter fullscreen mode Exit fullscreen mode

The meanings of the fields are clear. Here is an example:

func main() {
    users, _ := host.Users()
    for _, user := range users {
        data, _ := json.MarshalIndent(user, "", " ")
        fmt.Println(string(data))
    }
}
Enter fullscreen mode Exit fullscreen mode

Suppose the following is the output result after running the above code (the actual values will vary depending on the system status and user connection situation):

{
    "user": "leapcell",
    "terminal": "ttys001",
    "host": "localhost",
    "started": 565575675
}
Enter fullscreen mode Exit fullscreen mode

Memory

In the quick start section, we showed how to use mem.VirtualMemory() to obtain memory information, and this function only returns physical memory information. We can also use mem.SwapMemory() to obtain the information of swap memory, and the information is stored in the SwapMemoryStat structure:

// src/github.com/shirou/gopsutil/mem/
type SwapMemoryStat struct {
    Total       uint64  `json:"total"`
    Used        uint64  `json:"used"`
    Free        uint64  `json:"free"`
    UsedPercent float64 `json:"usedPercent"`
    Sin         uint64  `json:"sin"`
    Sout        uint64  `json:"sout"`
    PgIn        uint64  `json:"pgin"`
    PgOut       uint64  `json:"pgout"`
    PgFault     uint64  `json:"pgfault"`
}
Enter fullscreen mode Exit fullscreen mode

The meanings of these fields are relatively easy to understand. Among them, the three fields PgIn, PgOut, and PgFault need to be emphasized. Swap memory is in units of pages. If a page fault occurs, the operating system will load some pages from the disk into the memory, and at the same time, some pages in the memory will be eliminated according to a specific mechanism. PgIn represents the number of pages loaded, PgOut represents the number of pages eliminated, and PgFault is the number of page faults.

For example:

func main() {
    swapMemory, _ := mem.SwapMemory()
    data, _ := json.MarshalIndent(swapMemory, "", " ")
    fmt.Println(string(data))
}
Enter fullscreen mode Exit fullscreen mode

Suppose the following is the output result after running (the actual values depend on the system memory usage):

{
    "total": 8589934592,
    "used": 2147483648,
    "free": 6442450944,
    "usedPercent": 25.00,
    "sin": 1024,
    "sout": 512,
    "pgIn": 2048,
    "pgOut": 1536,
    "pgFault": 100
}
Enter fullscreen mode Exit fullscreen mode

Process

process can be used to obtain information about the processes currently running in the system, create new processes, and perform some operations on processes, etc.

func main() {
    var rootProcess *process.Process
    processes, _ := process.Processes()
    for _, p := range processes {
        if p.Pid == 0 {
            rootProcess = p
            break
        }
    }

    fmt.Println(rootProcess)

    fmt.Println("children:")
    children, _ := rootProcess.Children()
    for _, p := range children {
        fmt.Println(p)
    }
}
Enter fullscreen mode Exit fullscreen mode

The above code first calls process.Processes() to obtain all the processes running in the current system, then finds the process with Pid equal to 0 (on a Mac system, this process is usually the first process started by the kernel), and finally calls Children() to return its child processes. In addition, there are many methods that can be used to obtain process information, and users who are interested can refer to the relevant documents for further understanding.

Mac Services (Adjusted from the Original Windows Services Section)

The macservices sub-package (the original winservices) can obtain service information in the Mac system (assuming that such a sub-package and its functions exist). In macservices, a service corresponds to a Service structure (the following structure is assumed, and it may be different in reality):

// src/github.com/shirou/gopsutil/macservices/macservices.go
type Service struct {
    Name   string
    Config MacConfig
    Status ServiceStatus
    // contains filtered or unexported fields
}
Enter fullscreen mode Exit fullscreen mode

Among them, MacConfig (adjusted from the original mgr.Config for Mac) is an assumed structure, and this structure records in detail information such as the service type, startup type (automatic/manual), and binary file path (the assumed structure is as follows):

// src/github.com/shirou/gopsutil/macservices/macconfig.go
type MacConfig struct {
    ServiceType      string
    StartType        string
    BinaryPathName   string
    Dependencies     []string
    ServiceStartName string
    DisplayName      string
    Description      string
}
Enter fullscreen mode Exit fullscreen mode

The ServiceStatus structure records the status of the service (the assumed structure is as follows):

// src/github.com/shirou/gopsutil/macservices/macservices.go
type ServiceStatus struct {
    State         string
    Pid           uint32
    ExitCode      int
}
Enter fullscreen mode Exit fullscreen mode
  • State: Service status, including stopped, running, paused, etc.;
  • Pid: Process ID;
  • ExitCode: Application exit status code.

The following program outputs the names, binary file paths, and statuses of all services in the system to the console (the assumed code is as follows):

func main() {
    services, _ := macservices.ListServices()

    for _, service := range services {
        newservice, _ := macservices.NewService(service.Name)
        newservice.GetServiceDetail()
        fmt.Println("Name:", newservice.Name, "Binary Path:", newservice.Config.BinaryPathName, "State: ", newservice.Status.State)
    }
}
Enter fullscreen mode Exit fullscreen mode

It should be noted that the information of the Service object returned by calling macservices.ListServices() may not be complete. We create a service with the service name through NewService(), and then call the GetServiceDetail() method to obtain the detailed information of the service. We cannot directly call service.GetServiceDetail(), because the object returned by ListService() may lack the necessary system resource handles (to save resources), and calling the GetServiceDetail() method may cause program errors.

Errors and Timeouts

Since most functions involve underlying system calls, errors and timeouts are inevitable. Almost all interfaces have two return values, and the second return value is used to indicate an error. In the previous examples, to simplify the code, we ignored error handling. However, in actual use, it is recommended to handle errors properly.

In addition, most interfaces come in pairs. One does not have a parameter of type context.Context, and the other has this type of parameter for context control. It can handle errors or timeouts that occur during internal calls in a timely manner and avoid waiting for a long time for a return. In fact, functions without a context.Context parameter internally call functions with a context.Context parameter using context.Background() as the parameter. For example:

// src/github.com/shirou/gopsutil/cpu_mac.go
func Times(percpu bool) ([]TimesStat, error) {
    return TimesWithContext(context.Background(), percpu)
}

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The gopsutil library provides convenience for us to obtain local machine information, and it handles the compatibility issues between different systems well, providing a unified interface. There are also several sub-packages, such as net and docker, which are not introduced here due to space limitations. Users who are interested can explore them on their own.

Leapcell: The Next-Gen Serverless Platform for Web Hosting

Finally, I recommend the most suitable Golang deployment platform: leapcell

Image description

1. Multi-Language Support

  • Develop with JavaScript, Python, Go, or Rust.

2. Deploy unlimited projects for free

  • pay only for usage — no requests, no charges.

3. Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

4. Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

5. Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Image description

Explore more in the documentation!

Leapcell Twitter: https://x.com/LeapcellHQ

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay