(outputJSON bool)
| 47 | } |
| 48 | |
| 49 | func sendReport(outputJSON bool) error { |
| 50 | // Start tracking execution time |
| 51 | startTime := time.Now() |
| 52 | logger.Debug("Starting report process") |
| 53 | |
| 54 | // OPTIMIZATION: Force garbage collection before starting to free up memory |
| 55 | runtime.GC() |
| 56 | |
| 57 | // Load API credentials only if we're sending the report (not just outputting JSON) |
| 58 | if !outputJSON { |
| 59 | logger.Debug("Loading API credentials") |
| 60 | if err := cfgManager.LoadCredentials(); err != nil { |
| 61 | logger.WithError(err).Debug("Failed to load credentials") |
| 62 | return err |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // Initialise managers |
| 67 | systemDetector := system.New(logger) |
| 68 | packageMgr := packages.New(logger, packages.CacheRefreshConfig{ |
| 69 | Mode: cfgManager.GetPackageCacheRefreshMode(), |
| 70 | MaxAge: cfgManager.GetPackageCacheRefreshMaxAge(), |
| 71 | }) |
| 72 | repoMgr := repositories.New(logger) |
| 73 | hardwareMgr := hardware.New(logger) |
| 74 | networkMgr := network.New(logger) |
| 75 | |
| 76 | // OPTIMIZATION: Run all independent collectors concurrently. Each of these |
| 77 | // pieces of work is IO-bound (file reads, subprocess spawns) with no data |
| 78 | // dependency on the others, so a goroutine-per-task layout cuts wall time |
| 79 | // down to roughly max(task_duration) instead of sum(task_duration). |
| 80 | var ( |
| 81 | osType, osVersion string |
| 82 | osErr error |
| 83 | hostname string |
| 84 | hostnameErr error |
| 85 | architecture string |
| 86 | systemInfo models.SystemInfo |
| 87 | ipAddress string |
| 88 | hardwareInfo models.HardwareInfo |
| 89 | networkInfo models.NetworkInfo |
| 90 | needsReboot bool |
| 91 | rebootReason string |
| 92 | installedKernel string |
| 93 | packageList []models.Package |
| 94 | pkgErr error |
| 95 | repoList []models.Repository |
| 96 | repoErr error |
| 97 | machineID, detectedPackageMgr string |
| 98 | ) |
| 99 | |
| 100 | // Track panics from collector goroutines so that a panic in a critical |
| 101 | // task is escalated to a fatal error rather than silently producing an |
| 102 | // empty/partial report. |
| 103 | var ( |
| 104 | panicMu sync.Mutex |
| 105 | taskPanics = make(map[string]any) |
| 106 | ) |
no test coverage detected