disk.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package analytic
  2. import (
  3. "fmt"
  4. "github.com/dustin/go-humanize"
  5. "github.com/pkg/errors"
  6. "github.com/shirou/gopsutil/v4/disk"
  7. "github.com/spf13/cast"
  8. )
  9. func GetDiskStat() (DiskStat, error) {
  10. // Get all partitions
  11. partitions, err := disk.Partitions(false)
  12. if err != nil {
  13. return DiskStat{}, errors.Wrap(err, "error analytic getDiskStat - getting partitions")
  14. }
  15. var totalSize uint64
  16. var totalUsed uint64
  17. var partitionStats []PartitionStat
  18. // Track partitions to avoid double counting same partition with multiple mount points
  19. partitionUsage := make(map[string]*disk.UsageStat)
  20. // Get usage for each partition
  21. for _, partition := range partitions {
  22. usage, err := disk.Usage(partition.Mountpoint)
  23. if err != nil {
  24. // Skip partitions that can't be accessed
  25. continue
  26. }
  27. // Skip virtual filesystems and special filesystems
  28. if isVirtualFilesystem(partition.Fstype) {
  29. continue
  30. }
  31. // Create partition stat for display purposes
  32. partitionStat := PartitionStat{
  33. Mountpoint: partition.Mountpoint,
  34. Device: partition.Device,
  35. Fstype: partition.Fstype,
  36. Total: humanize.Bytes(usage.Total),
  37. Used: humanize.Bytes(usage.Used),
  38. Free: humanize.Bytes(usage.Free),
  39. Percentage: cast.ToFloat64(fmt.Sprintf("%.2f", usage.UsedPercent)),
  40. }
  41. partitionStats = append(partitionStats, partitionStat)
  42. // Only count each partition device once for total calculation
  43. // This handles cases where same partition is mounted multiple times (e.g., bind mounts, overlayfs)
  44. if _, exists := partitionUsage[partition.Device]; !exists {
  45. partitionUsage[partition.Device] = usage
  46. totalSize += usage.Total
  47. totalUsed += usage.Used
  48. }
  49. }
  50. // Calculate overall percentage
  51. var overallPercentage float64
  52. if totalSize > 0 {
  53. overallPercentage = cast.ToFloat64(fmt.Sprintf("%.2f", float64(totalUsed)/float64(totalSize)*100))
  54. }
  55. return DiskStat{
  56. Used: humanize.Bytes(totalUsed),
  57. Total: humanize.Bytes(totalSize),
  58. Percentage: overallPercentage,
  59. Writes: DiskWriteRecord[len(DiskWriteRecord)-1],
  60. Reads: DiskReadRecord[len(DiskReadRecord)-1],
  61. Partitions: partitionStats,
  62. }, nil
  63. }
  64. // isVirtualFilesystem checks if the filesystem type is virtual
  65. func isVirtualFilesystem(fstype string) bool {
  66. virtualFSTypes := map[string]bool{
  67. "proc": true,
  68. "sysfs": true,
  69. "devfs": true,
  70. "devpts": true,
  71. "tmpfs": true,
  72. "debugfs": true,
  73. "securityfs": true,
  74. "cgroup": true,
  75. "cgroup2": true,
  76. "pstore": true,
  77. "bpf": true,
  78. "tracefs": true,
  79. "hugetlbfs": true,
  80. "mqueue": true,
  81. "overlay": true,
  82. "autofs": true,
  83. "binfmt_misc": true,
  84. "configfs": true,
  85. "fusectl": true,
  86. "rpc_pipefs": true,
  87. "selinuxfs": true,
  88. "systemd-1": true,
  89. "none": true,
  90. }
  91. return virtualFSTypes[fstype]
  92. }