Gopsutil: Efficient System Monitoring in Go
Daniel Hayes
Full-Stack Engineer · Leapcell

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
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) }
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 originalwinservices
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}
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 infalse
to return the number of physical cores; pass intrue
to return the number of logical cores;Percent(interval time.Duration, percpu bool)
: Used to obtain the CPU usage within theinterval
time interval. Whenpercpu
isfalse
, obtain the total CPU usage; whenpercpu
istrue
, 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) }
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]
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)) } }
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": "" }
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"` }
CPU
: CPU identifier. If it is the total value, this field iscpu - total
, otherwise it iscpu0
,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)) } }
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 }
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"` // …… }
For example:
func main() { mapStat, _ := disk.IOCounters() for name, stat := range mapStat { fmt.Println(name) data, _ := json.MarshalIndent(stat, "", " ") fmt.Println(string(data)) } }
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" }
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"` }
Device
: Partition identifier. On Mac, for example, it is in the format ofdisk0s2
;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)) } }
The output on a Mac machine (only the first partition is shown, assumed value):
{ "device": "disk0s2", "mountpoint": "/", "fstype": "APFS", "opts": "rw" }
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"` }
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)) }
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 }
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")) }
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
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) }
The output when running on a Mac (assumed value):
22.6.0 platform: macOS 13.5 family: Darwin version: 22.6.0 ## Terminal Users `host.Users()` returns the information of users connected via the terminal, and each user corresponds to a `UserStat` structure: ```go // 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"` }
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)) } }
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 }
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"` }
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)) }
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 }
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) } }
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 }
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 }
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 }
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) } }
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) { // ... }
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, Async Tasks, and Redis
Finally, I recommend the most suitable Golang deployment platform: leapcell
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.
Explore more in the documentation!
Leapcell Twitter: https://x.com/LeapcellHQ