system_collector_windows.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. //go:build windows
  2. package diagnostic
  3. import (
  4. "context"
  5. "fmt"
  6. "os/exec"
  7. "strconv"
  8. )
  9. const kiloBytesScale = 1.0 / 1024
  10. type SystemCollectorImpl struct {
  11. version string
  12. }
  13. func NewSystemCollectorImpl(
  14. version string,
  15. ) *SystemCollectorImpl {
  16. return &SystemCollectorImpl{
  17. version,
  18. }
  19. }
  20. func (collector *SystemCollectorImpl) Collect(ctx context.Context) (*SystemInformation, string, error) {
  21. memoryInfo, memoryInfoRaw, memoryInfoErr := collectMemoryInformation(ctx)
  22. disks, disksRaw, diskErr := collectDiskVolumeInformation(ctx)
  23. osInfo, osInfoRaw, osInfoErr := collectOSInformation(ctx)
  24. if memoryInfoErr != nil {
  25. raw := RawSystemInformation(osInfoRaw, memoryInfoRaw, "", disksRaw)
  26. return nil, raw, memoryInfoErr
  27. }
  28. if diskErr != nil {
  29. raw := RawSystemInformation(osInfoRaw, memoryInfoRaw, "", disksRaw)
  30. return nil, raw, diskErr
  31. }
  32. if osInfoErr != nil {
  33. raw := RawSystemInformation(osInfoRaw, memoryInfoRaw, "", disksRaw)
  34. return nil, raw, osInfoErr
  35. }
  36. return NewSystemInformation(
  37. memoryInfo.MemoryMaximum,
  38. memoryInfo.MemoryCurrent,
  39. // For windows we leave both the fileDescriptorMaximum and fileDescriptorCurrent with zero
  40. // since there is no obvious way to get this information.
  41. 0,
  42. 0,
  43. osInfo.OsSystem,
  44. osInfo.Name,
  45. osInfo.OsVersion,
  46. osInfo.OsRelease,
  47. osInfo.Architecture,
  48. collector.version,
  49. disks,
  50. ), "", nil
  51. }
  52. func collectMemoryInformation(ctx context.Context) (*MemoryInformation, string, error) {
  53. const (
  54. memoryTotalPrefix = "TotalVirtualMemorySize"
  55. memoryAvailablePrefix = "FreeVirtualMemory"
  56. )
  57. command := exec.CommandContext(
  58. ctx,
  59. "powershell",
  60. "-Command",
  61. "Get-CimInstance -Class Win32_OperatingSystem | Select-Object FreeVirtualMemory, TotalVirtualMemorySize | Format-List",
  62. )
  63. stdout, err := command.Output()
  64. if err != nil {
  65. return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err)
  66. }
  67. output := string(stdout)
  68. // the result of the command above will return values in bytes hence
  69. // they need to be converted to kilobytes
  70. mapper := func(field string) (uint64, error) {
  71. value, err := strconv.ParseUint(field, 10, 64)
  72. return uint64(float64(value) * kiloBytesScale), err
  73. }
  74. memoryInfo, err := ParseMemoryInformationFromKV(output, memoryTotalPrefix, memoryAvailablePrefix, mapper)
  75. if err != nil {
  76. return nil, output, err
  77. }
  78. // returning raw output in case other collected information
  79. // resulted in errors
  80. return memoryInfo, output, nil
  81. }
  82. func collectDiskVolumeInformation(ctx context.Context) ([]*DiskVolumeInformation, string, error) {
  83. command := exec.CommandContext(
  84. ctx,
  85. "powershell", "-Command", "Get-CimInstance -Class Win32_LogicalDisk | Select-Object DeviceID, Size, FreeSpace")
  86. stdout, err := command.Output()
  87. if err != nil {
  88. return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err)
  89. }
  90. output := string(stdout)
  91. disks, err := ParseDiskVolumeInformationOutput(output, 2, kiloBytesScale)
  92. if err != nil {
  93. return nil, output, err
  94. }
  95. // returning raw output in case other collected information
  96. // resulted in errors
  97. return disks, output, nil
  98. }
  99. func collectOSInformation(ctx context.Context) (*OsInfo, string, error) {
  100. const (
  101. architecturePrefix = "OSArchitecture"
  102. osSystemPrefix = "Caption"
  103. osVersionPrefix = "Version"
  104. osReleasePrefix = "BuildNumber"
  105. namePrefix = "CSName"
  106. )
  107. command := exec.CommandContext(
  108. ctx,
  109. "powershell",
  110. "-Command",
  111. "Get-CimInstance -Class Win32_OperatingSystem | Select-Object OSArchitecture, Caption, Version, BuildNumber, CSName | Format-List",
  112. )
  113. stdout, err := command.Output()
  114. if err != nil {
  115. return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err)
  116. }
  117. output := string(stdout)
  118. osInfo, err := ParseWinOperatingSystemInfo(output, architecturePrefix, osSystemPrefix, osVersionPrefix, osReleasePrefix, namePrefix)
  119. if err != nil {
  120. return nil, output, err
  121. }
  122. // returning raw output in case other collected information
  123. // resulted in errors
  124. return osInfo, output, nil
  125. }