mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			438 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package machine
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
type FSInfo struct {
 | 
						|
	MountPoint string
 | 
						|
	Used       uint64
 | 
						|
	Free       uint64
 | 
						|
}
 | 
						|
 | 
						|
type NetIntfInfo struct {
 | 
						|
	IPv4 string
 | 
						|
	IPv6 string
 | 
						|
	Rx   uint64
 | 
						|
	Tx   uint64
 | 
						|
}
 | 
						|
 | 
						|
type cpuRaw struct {
 | 
						|
	User    uint64 // time spent in user mode
 | 
						|
	Nice    uint64 // time spent in user mode with low priority (nice)
 | 
						|
	System  uint64 // time spent in system mode
 | 
						|
	Idle    uint64 // time spent in the idle task
 | 
						|
	Iowait  uint64 // time spent waiting for I/O to complete (since Linux 2.5.41)
 | 
						|
	Irq     uint64 // time spent servicing  interrupts  (since  2.6.0-test4)
 | 
						|
	SoftIrq uint64 // time spent servicing softirqs (since 2.6.0-test4)
 | 
						|
	Steal   uint64 // time spent in other OSes when running in a virtualized environment
 | 
						|
	Guest   uint64 // time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
 | 
						|
	Total   uint64 // total of all time fields
 | 
						|
}
 | 
						|
 | 
						|
type CPUInfo struct {
 | 
						|
	User    float32
 | 
						|
	Nice    float32
 | 
						|
	System  float32
 | 
						|
	Idle    float32
 | 
						|
	Iowait  float32
 | 
						|
	Irq     float32
 | 
						|
	SoftIrq float32
 | 
						|
	Steal   float32
 | 
						|
	Guest   float32
 | 
						|
}
 | 
						|
 | 
						|
type Stats struct {
 | 
						|
	Uptime       time.Duration
 | 
						|
	Hostname     string
 | 
						|
	Load1        string
 | 
						|
	Load5        string
 | 
						|
	Load10       string
 | 
						|
	RunningProcs string
 | 
						|
	TotalProcs   string
 | 
						|
	MemTotal     uint64
 | 
						|
	MemFree      uint64
 | 
						|
	MemBuffers   uint64
 | 
						|
	MemCached    uint64
 | 
						|
	SwapTotal    uint64
 | 
						|
	SwapFree     uint64
 | 
						|
	FSInfos      []FSInfo
 | 
						|
	NetIntf      map[string]NetIntfInfo
 | 
						|
	CPU          CPUInfo // or []CPUInfo to get all the cpu-core's stats?
 | 
						|
}
 | 
						|
 | 
						|
func (c *Cli) GetAllStats() *Stats {
 | 
						|
	res, _ := c.Run(getShellContent("stats"))
 | 
						|
	infos := strings.Split(*res, "-----")
 | 
						|
	stats := new(Stats)
 | 
						|
	getUptime(infos[0], stats)
 | 
						|
	getHostname(infos[1], stats)
 | 
						|
	getLoad(infos[2], stats)
 | 
						|
	getMemInfo(infos[3], stats)
 | 
						|
	getFSInfo(infos[4], stats)
 | 
						|
	getInterfaces(infos[5], stats)
 | 
						|
	getInterfaceInfo(infos[6], stats)
 | 
						|
	getCPU(infos[7], stats)
 | 
						|
	return stats
 | 
						|
}
 | 
						|
 | 
						|
func getUptime(uptime string, stats *Stats) (err error) {
 | 
						|
	parts := strings.Fields(uptime)
 | 
						|
	if len(parts) == 2 {
 | 
						|
		var upsecs float64
 | 
						|
		upsecs, err = strconv.ParseFloat(parts[0], 64)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		stats.Uptime = time.Duration(upsecs * 1e9)
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getHostname(hostname string, stats *Stats) (err error) {
 | 
						|
	stats.Hostname = strings.TrimSpace(hostname)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getLoad(loadInfo string, stats *Stats) (err error) {
 | 
						|
	parts := strings.Fields(loadInfo)
 | 
						|
	if len(parts) == 5 {
 | 
						|
		stats.Load1 = parts[0]
 | 
						|
		stats.Load5 = parts[1]
 | 
						|
		stats.Load10 = parts[2]
 | 
						|
		if i := strings.Index(parts[3], "/"); i != -1 {
 | 
						|
			stats.RunningProcs = parts[3][0:i]
 | 
						|
			if i+1 < len(parts[3]) {
 | 
						|
				stats.TotalProcs = parts[3][i+1:]
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getMemInfo(memInfo string, stats *Stats) (err error) {
 | 
						|
	// "/bin/cat /proc/meminfo"
 | 
						|
	scanner := bufio.NewScanner(strings.NewReader(memInfo))
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		parts := strings.Fields(line)
 | 
						|
		if len(parts) == 3 {
 | 
						|
			val, err := strconv.ParseUint(parts[1], 10, 64)
 | 
						|
			if err != nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			val *= 1024
 | 
						|
			switch parts[0] {
 | 
						|
			case "MemTotal:":
 | 
						|
				stats.MemTotal = val
 | 
						|
			case "MemFree:":
 | 
						|
				stats.MemFree = val
 | 
						|
			case "Buffers:":
 | 
						|
				stats.MemBuffers = val
 | 
						|
			case "Cached:":
 | 
						|
				stats.MemCached = val
 | 
						|
			case "SwapTotal:":
 | 
						|
				stats.SwapTotal = val
 | 
						|
			case "SwapFree:":
 | 
						|
				stats.SwapFree = val
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getFSInfo(fsInfo string, stats *Stats) (err error) {
 | 
						|
	// "/bin/df -B1"
 | 
						|
	scanner := bufio.NewScanner(strings.NewReader(fsInfo))
 | 
						|
	flag := 0
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		parts := strings.Fields(line)
 | 
						|
		n := len(parts)
 | 
						|
		dev := n > 0 && strings.Index(parts[0], "/dev/") == 0
 | 
						|
		if n == 1 && dev {
 | 
						|
			flag = 1
 | 
						|
		} else if (n == 5 && flag == 1) || (n == 6 && dev) {
 | 
						|
			i := flag
 | 
						|
			flag = 0
 | 
						|
			used, err := strconv.ParseUint(parts[2-i], 10, 64)
 | 
						|
			if err != nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			free, err := strconv.ParseUint(parts[3-i], 10, 64)
 | 
						|
			if err != nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			stats.FSInfos = append(stats.FSInfos, FSInfo{
 | 
						|
				parts[5-i], used, free,
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getInterfaces(iInfo string, stats *Stats) (err error) {
 | 
						|
	// "/sbin/ip -o addr"
 | 
						|
	if stats.NetIntf == nil {
 | 
						|
		stats.NetIntf = make(map[string]NetIntfInfo)
 | 
						|
	}
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(strings.NewReader(iInfo))
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		parts := strings.Fields(line)
 | 
						|
		if len(parts) >= 4 && (parts[2] == "inet" || parts[2] == "inet6") {
 | 
						|
			ipv4 := parts[2] == "inet"
 | 
						|
			intfname := parts[1]
 | 
						|
			if info, ok := stats.NetIntf[intfname]; ok {
 | 
						|
				if ipv4 {
 | 
						|
					info.IPv4 = parts[3]
 | 
						|
				} else {
 | 
						|
					info.IPv6 = parts[3]
 | 
						|
				}
 | 
						|
				stats.NetIntf[intfname] = info
 | 
						|
			} else {
 | 
						|
				info := NetIntfInfo{}
 | 
						|
				if ipv4 {
 | 
						|
					info.IPv4 = parts[3]
 | 
						|
				} else {
 | 
						|
					info.IPv6 = parts[3]
 | 
						|
				}
 | 
						|
				stats.NetIntf[intfname] = info
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func getInterfaceInfo(iInfo string, stats *Stats) (err error) {
 | 
						|
	// /bin/cat /proc/net/dev
 | 
						|
	if stats.NetIntf == nil {
 | 
						|
		return
 | 
						|
	} // should have been here already
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(strings.NewReader(iInfo))
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		parts := strings.Fields(line)
 | 
						|
		if len(parts) == 17 {
 | 
						|
			intf := strings.TrimSpace(parts[0])
 | 
						|
			intf = strings.TrimSuffix(intf, ":")
 | 
						|
			if info, ok := stats.NetIntf[intf]; ok {
 | 
						|
				rx, err := strconv.ParseUint(parts[1], 10, 64)
 | 
						|
				if err != nil {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				tx, err := strconv.ParseUint(parts[9], 10, 64)
 | 
						|
				if err != nil {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				info.Rx = rx
 | 
						|
				info.Tx = tx
 | 
						|
				stats.NetIntf[intf] = info
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func parseCPUFields(fields []string, stat *cpuRaw) {
 | 
						|
	numFields := len(fields)
 | 
						|
	for i := 1; i < numFields; i++ {
 | 
						|
		val, err := strconv.ParseUint(fields[i], 10, 64)
 | 
						|
		if err != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		stat.Total += val
 | 
						|
		switch i {
 | 
						|
		case 1:
 | 
						|
			stat.User = val
 | 
						|
		case 2:
 | 
						|
			stat.Nice = val
 | 
						|
		case 3:
 | 
						|
			stat.System = val
 | 
						|
		case 4:
 | 
						|
			stat.Idle = val
 | 
						|
		case 5:
 | 
						|
			stat.Iowait = val
 | 
						|
		case 6:
 | 
						|
			stat.Irq = val
 | 
						|
		case 7:
 | 
						|
			stat.SoftIrq = val
 | 
						|
		case 8:
 | 
						|
			stat.Steal = val
 | 
						|
		case 9:
 | 
						|
			stat.Guest = val
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// the CPU stats that were fetched last time round
 | 
						|
var preCPU cpuRaw
 | 
						|
 | 
						|
func getCPU(cpuInfo string, stats *Stats) (err error) {
 | 
						|
	var (
 | 
						|
		nowCPU cpuRaw
 | 
						|
		total  float32
 | 
						|
	)
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(strings.NewReader(cpuInfo))
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		fields := strings.Fields(line)
 | 
						|
		if len(fields) > 0 && fields[0] == "cpu" { // changing here if want to get every cpu-core's stats
 | 
						|
			parseCPUFields(fields, &nowCPU)
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if preCPU.Total == 0 { // having no pre raw cpu data
 | 
						|
		goto END
 | 
						|
	}
 | 
						|
 | 
						|
	total = float32(nowCPU.Total - preCPU.Total)
 | 
						|
	stats.CPU.User = float32(nowCPU.User-preCPU.User) / total * 100
 | 
						|
	stats.CPU.Nice = float32(nowCPU.Nice-preCPU.Nice) / total * 100
 | 
						|
	stats.CPU.System = float32(nowCPU.System-preCPU.System) / total * 100
 | 
						|
	stats.CPU.Idle = float32(nowCPU.Idle-preCPU.Idle) / total * 100
 | 
						|
	stats.CPU.Iowait = float32(nowCPU.Iowait-preCPU.Iowait) / total * 100
 | 
						|
	stats.CPU.Irq = float32(nowCPU.Irq-preCPU.Irq) / total * 100
 | 
						|
	stats.CPU.SoftIrq = float32(nowCPU.SoftIrq-preCPU.SoftIrq) / total * 100
 | 
						|
	stats.CPU.Guest = float32(nowCPU.Guest-preCPU.Guest) / total * 100
 | 
						|
END:
 | 
						|
	preCPU = nowCPU
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func fmtUptime(stats *Stats) string {
 | 
						|
	dur := stats.Uptime
 | 
						|
	dur = dur - (dur % time.Second)
 | 
						|
	var days int
 | 
						|
	for dur.Hours() > 24.0 {
 | 
						|
		days++
 | 
						|
		dur -= 24 * time.Hour
 | 
						|
	}
 | 
						|
	s1 := dur.String()
 | 
						|
	s2 := ""
 | 
						|
	if days > 0 {
 | 
						|
		s2 = fmt.Sprintf("%dd ", days)
 | 
						|
	}
 | 
						|
	for _, ch := range s1 {
 | 
						|
		s2 += string(ch)
 | 
						|
		if ch == 'h' || ch == 'm' {
 | 
						|
			s2 += " "
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return s2
 | 
						|
}
 | 
						|
 | 
						|
func fmtBytes(val uint64) string {
 | 
						|
	if val < 1024 {
 | 
						|
		return fmt.Sprintf("%d bytes", val)
 | 
						|
	} else if val < 1024*1024 {
 | 
						|
		return fmt.Sprintf("%6.2f KiB", float64(val)/1024.0)
 | 
						|
	} else if val < 1024*1024*1024 {
 | 
						|
		return fmt.Sprintf("%6.2f MiB", float64(val)/1024.0/1024.0)
 | 
						|
	} else {
 | 
						|
		return fmt.Sprintf("%6.2f GiB", float64(val)/1024.0/1024.0/1024.0)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func ShowStats(output io.Writer, stats *Stats) {
 | 
						|
	used := stats.MemTotal - stats.MemFree - stats.MemBuffers - stats.MemCached
 | 
						|
	fmt.Fprintf(output,
 | 
						|
		`%s%s%s%s up %s%s%s
 | 
						|
Load:
 | 
						|
    %s%s %s %s%s
 | 
						|
CPU:
 | 
						|
    %s%.2f%s%% user, %s%.2f%s%% sys, %s%.2f%s%% nice, %s%.2f%s%% idle, %s%.2f%s%% iowait, %s%.2f%s%% hardirq, %s%.2f%s%% softirq, %s%.2f%s%% guest
 | 
						|
Processes:
 | 
						|
    %s%s%s running of %s%s%s total
 | 
						|
Memory:
 | 
						|
    free    = %s%s%s
 | 
						|
    used    = %s%s%s
 | 
						|
    buffers = %s%s%s
 | 
						|
    cached  = %s%s%s
 | 
						|
    swap    = %s%s%s free of %s%s%s
 | 
						|
`,
 | 
						|
		escClear,
 | 
						|
		escBrightWhite, stats.Hostname, escReset,
 | 
						|
		escBrightWhite, fmtUptime(stats), escReset,
 | 
						|
		escBrightWhite, stats.Load1, stats.Load5, stats.Load10, escReset,
 | 
						|
		escBrightWhite, stats.CPU.User, escReset,
 | 
						|
		escBrightWhite, stats.CPU.System, escReset,
 | 
						|
		escBrightWhite, stats.CPU.Nice, escReset,
 | 
						|
		escBrightWhite, stats.CPU.Idle, escReset,
 | 
						|
		escBrightWhite, stats.CPU.Iowait, escReset,
 | 
						|
		escBrightWhite, stats.CPU.Irq, escReset,
 | 
						|
		escBrightWhite, stats.CPU.SoftIrq, escReset,
 | 
						|
		escBrightWhite, stats.CPU.Guest, escReset,
 | 
						|
		escBrightWhite, stats.RunningProcs, escReset,
 | 
						|
		escBrightWhite, stats.TotalProcs, escReset,
 | 
						|
		escBrightWhite, fmtBytes(stats.MemFree), escReset,
 | 
						|
		escBrightWhite, fmtBytes(used), escReset,
 | 
						|
		escBrightWhite, fmtBytes(stats.MemBuffers), escReset,
 | 
						|
		escBrightWhite, fmtBytes(stats.MemCached), escReset,
 | 
						|
		escBrightWhite, fmtBytes(stats.SwapFree), escReset,
 | 
						|
		escBrightWhite, fmtBytes(stats.SwapTotal), escReset,
 | 
						|
	)
 | 
						|
	if len(stats.FSInfos) > 0 {
 | 
						|
		fmt.Println("Filesystems:")
 | 
						|
		for _, fs := range stats.FSInfos {
 | 
						|
			fmt.Fprintf(output, "    %s%8s%s: %s%s%s free of %s%s%s\n",
 | 
						|
				escBrightWhite, fs.MountPoint, escReset,
 | 
						|
				escBrightWhite, fmtBytes(fs.Free), escReset,
 | 
						|
				escBrightWhite, fmtBytes(fs.Used+fs.Free), escReset,
 | 
						|
			)
 | 
						|
		}
 | 
						|
		fmt.Println()
 | 
						|
	}
 | 
						|
	if len(stats.NetIntf) > 0 {
 | 
						|
		fmt.Println("Network Interfaces:")
 | 
						|
		keys := make([]string, 0, len(stats.NetIntf))
 | 
						|
		for intf := range stats.NetIntf {
 | 
						|
			keys = append(keys, intf)
 | 
						|
		}
 | 
						|
		sort.Strings(keys)
 | 
						|
		for _, intf := range keys {
 | 
						|
			info := stats.NetIntf[intf]
 | 
						|
			fmt.Fprintf(output, "    %s%s%s - %s%s%s",
 | 
						|
				escBrightWhite, intf, escReset,
 | 
						|
				escBrightWhite, info.IPv4, escReset,
 | 
						|
			)
 | 
						|
			if len(info.IPv6) > 0 {
 | 
						|
				fmt.Fprintf(output, ", %s%s%s\n",
 | 
						|
					escBrightWhite, info.IPv6, escReset,
 | 
						|
				)
 | 
						|
			} else {
 | 
						|
				fmt.Fprintf(output, "\n")
 | 
						|
			}
 | 
						|
			fmt.Fprintf(output, "      rx = %s%s%s, tx = %s%s%s\n",
 | 
						|
				escBrightWhite, fmtBytes(info.Rx), escReset,
 | 
						|
				escBrightWhite, fmtBytes(info.Tx), escReset,
 | 
						|
			)
 | 
						|
			fmt.Println()
 | 
						|
		}
 | 
						|
		fmt.Println()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	escClear       = ""
 | 
						|
	escRed         = ""
 | 
						|
	escReset       = ""
 | 
						|
	escBrightWhite = ""
 | 
						|
)
 |