refactor CollectorCoordinator (method logic extraction)

This commit is contained in:
Oliver Kieselbach
2026-04-14 01:00:24 +02:00
parent 918d6c0ee7
commit bb3efae5dc
4 changed files with 450 additions and 286 deletions
@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutopilotMonitor.Agent.Core.Configuration;
using AutopilotMonitor.Agent.Core.Logging;
using AutopilotMonitor.Agent.Core.Monitoring.Analyzers;
using AutopilotMonitor.Shared.Models;
namespace AutopilotMonitor.Agent.Core.Monitoring.Core
{
/// <summary>
/// Manages agent analyzers: initialization from remote config, startup execution
/// on a background thread, and shutdown execution with optional WhiteGlove part.
/// </summary>
public class AnalyzerManager
{
private readonly AgentConfiguration _configuration;
private readonly AgentLogger _logger;
private readonly Action<EnrollmentEvent> _emitEvent;
private readonly RemoteConfigService _remoteConfigService;
private readonly List<IAgentAnalyzer> _analyzers = new List<IAgentAnalyzer>();
public AnalyzerManager(
AgentConfiguration configuration,
AgentLogger logger,
Action<EnrollmentEvent> emitEvent,
RemoteConfigService remoteConfigService)
{
_configuration = configuration;
_logger = logger;
_emitEvent = emitEvent;
_remoteConfigService = remoteConfigService;
}
public void Initialize()
{
_analyzers.Clear();
var analyzerConfig = _remoteConfigService?.CurrentConfig?.Analyzers ?? new AnalyzerConfiguration();
if (analyzerConfig.EnableLocalAdminAnalyzer)
{
_analyzers.Add(new LocalAdminAnalyzer(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
analyzerConfig.LocalAdminAllowedAccounts
));
_logger.Info("LocalAdminAnalyzer registered");
}
else
{
_logger.Info("LocalAdminAnalyzer disabled by remote config");
}
if (analyzerConfig.EnableSoftwareInventoryAnalyzer)
{
_analyzers.Add(new SoftwareInventoryAnalyzer(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger
));
_logger.Info("SoftwareInventoryAnalyzer registered");
}
else
{
_logger.Info("SoftwareInventoryAnalyzer disabled by remote config");
}
_logger.Info($"Analyzers initialized: {_analyzers.Count} active");
}
public void RunStartup()
{
if (_analyzers.Count == 0)
return;
var analyzers = new List<IAgentAnalyzer>(_analyzers);
_logger.Info($"Scheduling {analyzers.Count} startup analyzer(s) on background thread");
Task.Run(() =>
{
foreach (var analyzer in analyzers)
{
try
{
analyzer.AnalyzeAtStartup();
}
catch (Exception ex)
{
_logger.Error($"Analyzer {analyzer.Name} threw during startup", ex);
}
}
});
}
public void RunShutdown(int? whiteGlovePart = null)
{
if (_analyzers.Count == 0)
return;
_logger.Info($"Running {_analyzers.Count} shutdown analyzer(s) (whiteGlovePart={whiteGlovePart?.ToString() ?? "none"})");
foreach (var analyzer in _analyzers)
{
try
{
if (analyzer is SoftwareInventoryAnalyzer softwareAnalyzer)
softwareAnalyzer.AnalyzeAtShutdown(whiteGlovePart);
else
analyzer.AnalyzeAtShutdown();
}
catch (Exception ex)
{
_logger.Error($"Analyzer {analyzer.Name} threw during shutdown", ex);
}
}
}
}
}
@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AutopilotMonitor.Agent.Core.Configuration;
using AutopilotMonitor.Agent.Core.Logging;
using AutopilotMonitor.Agent.Core.Monitoring.Analyzers;
using AutopilotMonitor.Agent.Core.Monitoring.Collectors;
using AutopilotMonitor.Agent.Core.Monitoring.Network;
using AutopilotMonitor.Agent.Core.Monitoring.Replay;
@@ -39,11 +37,12 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
private LogReplayService _logReplay;
// Optional collectors (toggled via remote config)
private PerformanceCollector _performanceCollector;
private AgentSelfMetricsCollector _agentSelfMetricsCollector;
private DeliveryOptimizationCollector _deliveryOptimizationCollector;
private StallProbeCollector _stallProbeCollector;
// Periodic collectors + idle management
private PeriodicCollectorManager _periodicManager;
// Smart enrollment tracking
private EnrollmentTracker _enrollmentTracker;
@@ -56,13 +55,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
private NetworkChangeDetector _networkChangeDetector;
// Analyzers
private readonly List<IAgentAnalyzer> _analyzers = new List<IAgentAnalyzer>();
// Idle detection
private DateTime _lastRealEventTime = DateTime.UtcNow;
private bool _collectorsIdleStopped;
private int _collectorIdleTimeoutMinutes = 15;
private Timer _idleCheckTimer;
private AnalyzerManager _analyzerManager;
// Max lifetime safety net
private Timer _maxLifetimeTimer;
@@ -98,7 +91,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
/// <summary>
/// Whether periodic collectors are currently paused due to idle timeout.
/// </summary>
public bool CollectorsIdleStopped => _collectorsIdleStopped;
public bool CollectorsIdleStopped => _periodicManager?.CollectorsIdleStopped ?? false;
/// <summary>
/// Called from EmitEvent when a non-periodic event is received.
@@ -106,15 +99,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
/// </summary>
public void OnRealEventReceived()
{
_lastRealEventTime = DateTime.UtcNow;
try { _stallProbeCollector?.ResetProbes(); }
catch (Exception ex) { _logger.Verbose($"StallProbeCollector.ResetProbes failed: {ex.Message}"); }
if (_collectorsIdleStopped)
{
RestartPeriodicCollectors();
}
_periodicManager?.OnRealEventReceived();
}
/// <summary>
@@ -148,9 +133,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
/// </summary>
public void StopPerformanceCollectorOnly()
{
_performanceCollector?.Stop();
_performanceCollector?.Dispose();
_performanceCollector = null;
_periodicManager?.StopPerformanceCollectorOnly();
}
/// <summary>
@@ -166,13 +149,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
/// </summary>
public static bool IsPeriodicEvent(string eventType)
{
return eventType == "performance_snapshot" ||
eventType == "agent_metrics_snapshot" ||
eventType == "performance_collector_stopped" ||
eventType == "agent_metrics_collector_stopped" ||
eventType == Constants.EventTypes.StallProbeCheck ||
eventType == Constants.EventTypes.StallProbeResult ||
eventType == Constants.EventTypes.SessionStalled;
return PeriodicCollectorManager.IsPeriodicEvent(eventType);
}
public void StartEventCollectors()
@@ -235,37 +212,10 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
try
{
var perfInterval = collectors?.PerformanceIntervalSeconds ?? 60;
_performanceCollector = new PerformanceCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
perfInterval
);
_performanceCollector.Start();
_logger.Info($"PerformanceCollector started (interval={perfInterval}s)");
if (collectors?.EnableAgentSelfMetrics != false)
{
var selfMetricsInterval = collectors?.AgentSelfMetricsIntervalSeconds ?? 60;
_agentSelfMetricsCollector = new AgentSelfMetricsCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_apiClient.NetworkMetrics,
_spool,
_logger,
_agentVersion,
selfMetricsInterval
);
_agentSelfMetricsCollector.Start();
_logger.Info($"AgentSelfMetricsCollector started (interval={selfMetricsInterval}s)");
}
_collectorIdleTimeoutMinutes = collectors?.CollectorIdleTimeoutMinutes ?? 15;
_lastRealEventTime = DateTime.UtcNow;
_collectorsIdleStopped = false;
_periodicManager = new PeriodicCollectorManager(
_configuration, _logger, _agentVersion,
_emitEvent, _apiClient, _spool, _remoteConfigService);
_periodicManager.Start();
if (collectors?.StallProbeEnabled != false)
{
@@ -292,16 +242,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
_logger.Info("StallProbeCollector disabled via config");
}
_idleCheckTimer = new Timer(
IdleCheckCallback,
null,
TimeSpan.FromSeconds(60),
TimeSpan.FromSeconds(60)
);
if (_collectorIdleTimeoutMinutes > 0)
_logger.Info($"Collector idle timeout enabled: {_collectorIdleTimeoutMinutes} min");
else
_logger.Info("Collector idle timeout disabled (0)");
_periodicManager.StallProbeCollector = _stallProbeCollector;
if (string.IsNullOrEmpty(_configuration.ReplayLogDir))
{
@@ -421,8 +362,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
public void StopOptionalCollectors()
{
_idleCheckTimer?.Dispose();
_idleCheckTimer = null;
_periodicManager?.Stop();
_maxLifetimeTimer?.Dispose();
_maxLifetimeTimer = null;
@@ -440,14 +380,6 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
_networkChangeDetector?.Dispose();
_networkChangeDetector = null;
_performanceCollector?.Stop();
_performanceCollector?.Dispose();
_performanceCollector = null;
_agentSelfMetricsCollector?.Stop();
_agentSelfMetricsCollector?.Dispose();
_agentSelfMetricsCollector = null;
_deliveryOptimizationCollector?.Stop();
_deliveryOptimizationCollector?.Dispose();
_deliveryOptimizationCollector = null;
@@ -512,90 +444,18 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
public void InitializeAnalyzers()
{
_analyzers.Clear();
var analyzerConfig = _remoteConfigService?.CurrentConfig?.Analyzers ?? new AnalyzerConfiguration();
if (analyzerConfig.EnableLocalAdminAnalyzer)
{
_analyzers.Add(new LocalAdminAnalyzer(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
analyzerConfig.LocalAdminAllowedAccounts
));
_logger.Info("LocalAdminAnalyzer registered");
}
else
{
_logger.Info("LocalAdminAnalyzer disabled by remote config");
}
if (analyzerConfig.EnableSoftwareInventoryAnalyzer)
{
_analyzers.Add(new SoftwareInventoryAnalyzer(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger
));
_logger.Info("SoftwareInventoryAnalyzer registered");
}
else
{
_logger.Info("SoftwareInventoryAnalyzer disabled by remote config");
}
_logger.Info($"Analyzers initialized: {_analyzers.Count} active");
_analyzerManager = new AnalyzerManager(_configuration, _logger, _emitEvent, _remoteConfigService);
_analyzerManager.Initialize();
}
public void RunStartupAnalyzers()
{
if (_analyzers.Count == 0)
return;
var analyzers = new List<IAgentAnalyzer>(_analyzers);
_logger.Info($"Scheduling {analyzers.Count} startup analyzer(s) on background thread");
Task.Run(() =>
{
foreach (var analyzer in analyzers)
{
try
{
analyzer.AnalyzeAtStartup();
}
catch (Exception ex)
{
_logger.Error($"Analyzer {analyzer.Name} threw during startup", ex);
}
}
});
_analyzerManager?.RunStartup();
}
public void RunShutdownAnalyzers(int? whiteGlovePart = null)
{
if (_analyzers.Count == 0)
return;
_logger.Info($"Running {_analyzers.Count} shutdown analyzer(s) (whiteGlovePart={whiteGlovePart?.ToString() ?? "none"})");
foreach (var analyzer in _analyzers)
{
try
{
if (analyzer is SoftwareInventoryAnalyzer softwareAnalyzer)
softwareAnalyzer.AnalyzeAtShutdown(whiteGlovePart);
else
analyzer.AnalyzeAtShutdown();
}
catch (Exception ex)
{
_logger.Error($"Analyzer {analyzer.Name} threw during shutdown", ex);
}
}
_analyzerManager?.RunShutdown(whiteGlovePart);
}
/// <summary>
@@ -659,138 +519,12 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
_enrollmentTracker?.NotifyDesktopArrived();
}
private void IdleCheckCallback(object state)
{
var idleMinutes = (DateTime.UtcNow - _lastRealEventTime).TotalMinutes;
try
{
_stallProbeCollector?.CheckAndRunProbes(idleMinutes);
}
catch (Exception ex)
{
_logger.Error("StallProbeCollector.CheckAndRunProbes threw", ex);
}
if (_collectorsIdleStopped)
return;
if (idleMinutes < _collectorIdleTimeoutMinutes)
return;
_logger.Info($"Collector idle timeout reached ({_collectorIdleTimeoutMinutes} min, idle for {idleMinutes:F0} min) — stopping periodic collectors");
if (_performanceCollector != null)
{
_performanceCollector.Stop();
_performanceCollector.Dispose();
_performanceCollector = null;
_emitEvent(new EnrollmentEvent
{
SessionId = _configuration.SessionId,
TenantId = _configuration.TenantId,
Timestamp = DateTime.UtcNow,
EventType = "performance_collector_stopped",
Severity = EventSeverity.Info,
Source = "MonitoringService",
Phase = EnrollmentPhase.Unknown,
Message = $"Performance collector stopped after {_collectorIdleTimeoutMinutes} min idle (no real enrollment activity)",
Data = new Dictionary<string, object>
{
{ "reason", "idle_timeout" },
{ "idleTimeoutMinutes", _collectorIdleTimeoutMinutes },
{ "idleMinutes", Math.Round(idleMinutes, 1) }
}
});
}
if (_agentSelfMetricsCollector != null)
{
_agentSelfMetricsCollector.Stop();
_agentSelfMetricsCollector.Dispose();
_agentSelfMetricsCollector = null;
_emitEvent(new EnrollmentEvent
{
SessionId = _configuration.SessionId,
TenantId = _configuration.TenantId,
Timestamp = DateTime.UtcNow,
EventType = "agent_metrics_collector_stopped",
Severity = EventSeverity.Info,
Source = "MonitoringService",
Phase = EnrollmentPhase.Unknown,
Message = $"AgentSelfMetrics collector stopped after {_collectorIdleTimeoutMinutes} min idle (no real enrollment activity)",
Data = new Dictionary<string, object>
{
{ "reason", "idle_timeout" },
{ "idleTimeoutMinutes", _collectorIdleTimeoutMinutes },
{ "idleMinutes", Math.Round(idleMinutes, 1) }
}
});
}
_collectorsIdleStopped = true;
}
private void RestartPeriodicCollectors()
{
var config = _remoteConfigService?.CurrentConfig;
var collectors = config?.Collectors;
_logger.Info("Restarting periodic collectors — new enrollment activity detected after idle stop");
try
{
var perfInterval = collectors?.PerformanceIntervalSeconds ?? 60;
_performanceCollector = new PerformanceCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
perfInterval
);
_performanceCollector.Start();
if (collectors?.EnableAgentSelfMetrics != false)
{
var selfMetricsInterval = collectors?.AgentSelfMetricsIntervalSeconds ?? 60;
_agentSelfMetricsCollector = new AgentSelfMetricsCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_apiClient.NetworkMetrics,
_spool,
_logger,
_agentVersion,
selfMetricsInterval
);
_agentSelfMetricsCollector.Start();
}
_lastRealEventTime = DateTime.UtcNow;
_collectorsIdleStopped = false;
if (_collectorIdleTimeoutMinutes > 0)
{
_idleCheckTimer?.Change(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
}
_logger.Info("Periodic collectors restarted successfully");
}
catch (Exception ex)
{
_logger.Error("Error restarting periodic collectors", ex);
}
}
public void Dispose()
{
_idleCheckTimer?.Dispose();
_periodicManager?.Dispose();
_maxLifetimeTimer?.Dispose();
_espAndHelloTracker?.Dispose();
_logReplay?.Dispose();
_performanceCollector?.Dispose();
_agentSelfMetricsCollector?.Dispose();
_deliveryOptimizationCollector?.Dispose();
_enrollmentTracker?.Dispose();
_networkChangeDetector?.Dispose();
@@ -0,0 +1,306 @@
using System;
using System.Collections.Generic;
using System.Threading;
using AutopilotMonitor.Agent.Core.Configuration;
using AutopilotMonitor.Agent.Core.Logging;
using AutopilotMonitor.Agent.Core.Monitoring.Collectors;
using AutopilotMonitor.Agent.Core.Monitoring.Network;
using AutopilotMonitor.Shared;
using AutopilotMonitor.Shared.Models;
namespace AutopilotMonitor.Agent.Core.Monitoring.Core
{
/// <summary>
/// Manages periodic collectors (PerformanceCollector, AgentSelfMetricsCollector) and their
/// idle lifecycle: stops them after inactivity, restarts when real enrollment activity resumes.
/// </summary>
public class PeriodicCollectorManager : IDisposable
{
private readonly AgentConfiguration _configuration;
private readonly AgentLogger _logger;
private readonly string _agentVersion;
private readonly Action<EnrollmentEvent> _emitEvent;
private readonly BackendApiClient _apiClient;
private readonly EventSpool _spool;
private readonly RemoteConfigService _remoteConfigService;
private PerformanceCollector _performanceCollector;
private AgentSelfMetricsCollector _agentSelfMetricsCollector;
private DateTime _lastRealEventTime = DateTime.UtcNow;
private bool _collectorsIdleStopped;
private int _collectorIdleTimeoutMinutes = 15;
private Timer _idleCheckTimer;
/// <summary>
/// Optional reference to the StallProbeCollector for resetting/running probes
/// during idle checks. Set after creation in StartOptionalCollectors.
/// </summary>
public StallProbeCollector StallProbeCollector { get; set; }
public PeriodicCollectorManager(
AgentConfiguration configuration,
AgentLogger logger,
string agentVersion,
Action<EnrollmentEvent> emitEvent,
BackendApiClient apiClient,
EventSpool spool,
RemoteConfigService remoteConfigService)
{
_configuration = configuration;
_logger = logger;
_agentVersion = agentVersion;
_emitEvent = emitEvent;
_apiClient = apiClient;
_spool = spool;
_remoteConfigService = remoteConfigService;
}
/// <summary>
/// Whether periodic collectors are currently paused due to idle timeout.
/// </summary>
public bool CollectorsIdleStopped => _collectorsIdleStopped;
/// <summary>
/// Starts the performance and metrics collectors and arms the idle check timer.
/// Called from CollectorCoordinator.StartOptionalCollectors.
/// </summary>
public void Start()
{
var config = _remoteConfigService?.CurrentConfig;
var collectors = config?.Collectors;
var perfInterval = collectors?.PerformanceIntervalSeconds ?? 60;
_performanceCollector = new PerformanceCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
perfInterval
);
_performanceCollector.Start();
_logger.Info($"PerformanceCollector started (interval={perfInterval}s)");
if (collectors?.EnableAgentSelfMetrics != false)
{
var selfMetricsInterval = collectors?.AgentSelfMetricsIntervalSeconds ?? 60;
_agentSelfMetricsCollector = new AgentSelfMetricsCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_apiClient.NetworkMetrics,
_spool,
_logger,
_agentVersion,
selfMetricsInterval
);
_agentSelfMetricsCollector.Start();
_logger.Info($"AgentSelfMetricsCollector started (interval={selfMetricsInterval}s)");
}
_collectorIdleTimeoutMinutes = collectors?.CollectorIdleTimeoutMinutes ?? 15;
_lastRealEventTime = DateTime.UtcNow;
_collectorsIdleStopped = false;
_idleCheckTimer = new Timer(
IdleCheckCallback,
null,
TimeSpan.FromSeconds(60),
TimeSpan.FromSeconds(60)
);
if (_collectorIdleTimeoutMinutes > 0)
_logger.Info($"Collector idle timeout enabled: {_collectorIdleTimeoutMinutes} min");
else
_logger.Info("Collector idle timeout disabled (0)");
}
/// <summary>
/// Called when a non-periodic event is received. Updates idle tracking,
/// resets stall probes, and restarts paused collectors.
/// </summary>
public void OnRealEventReceived()
{
_lastRealEventTime = DateTime.UtcNow;
try { StallProbeCollector?.ResetProbes(); }
catch (Exception ex) { _logger.Verbose($"StallProbeCollector.ResetProbes failed: {ex.Message}"); }
if (_collectorsIdleStopped)
{
RestartPeriodicCollectors();
}
}
/// <summary>
/// Stops just the performance collector (used during WhiteGlove completion).
/// </summary>
public void StopPerformanceCollectorOnly()
{
_performanceCollector?.Stop();
_performanceCollector?.Dispose();
_performanceCollector = null;
}
/// <summary>
/// Stops all periodic collectors and the idle check timer.
/// </summary>
public void Stop()
{
_idleCheckTimer?.Dispose();
_idleCheckTimer = null;
_performanceCollector?.Stop();
_performanceCollector?.Dispose();
_performanceCollector = null;
_agentSelfMetricsCollector?.Stop();
_agentSelfMetricsCollector?.Dispose();
_agentSelfMetricsCollector = null;
}
/// <summary>
/// Returns true for events generated by periodic timers (not real enrollment activity).
/// </summary>
public static bool IsPeriodicEvent(string eventType)
{
return eventType == "performance_snapshot" ||
eventType == "agent_metrics_snapshot" ||
eventType == "performance_collector_stopped" ||
eventType == "agent_metrics_collector_stopped" ||
eventType == Constants.EventTypes.StallProbeCheck ||
eventType == Constants.EventTypes.StallProbeResult ||
eventType == Constants.EventTypes.SessionStalled;
}
private void IdleCheckCallback(object state)
{
var idleMinutes = (DateTime.UtcNow - _lastRealEventTime).TotalMinutes;
try
{
StallProbeCollector?.CheckAndRunProbes(idleMinutes);
}
catch (Exception ex)
{
_logger.Error("StallProbeCollector.CheckAndRunProbes threw", ex);
}
if (_collectorsIdleStopped)
return;
if (idleMinutes < _collectorIdleTimeoutMinutes)
return;
_logger.Info($"Collector idle timeout reached ({_collectorIdleTimeoutMinutes} min, idle for {idleMinutes:F0} min) — stopping periodic collectors");
if (_performanceCollector != null)
{
_performanceCollector.Stop();
_performanceCollector.Dispose();
_performanceCollector = null;
_emitEvent(new EnrollmentEvent
{
SessionId = _configuration.SessionId,
TenantId = _configuration.TenantId,
Timestamp = DateTime.UtcNow,
EventType = "performance_collector_stopped",
Severity = EventSeverity.Info,
Source = "MonitoringService",
Phase = EnrollmentPhase.Unknown,
Message = $"Performance collector stopped after {_collectorIdleTimeoutMinutes} min idle (no real enrollment activity)",
Data = new Dictionary<string, object>
{
{ "reason", "idle_timeout" },
{ "idleTimeoutMinutes", _collectorIdleTimeoutMinutes },
{ "idleMinutes", Math.Round(idleMinutes, 1) }
}
});
}
if (_agentSelfMetricsCollector != null)
{
_agentSelfMetricsCollector.Stop();
_agentSelfMetricsCollector.Dispose();
_agentSelfMetricsCollector = null;
_emitEvent(new EnrollmentEvent
{
SessionId = _configuration.SessionId,
TenantId = _configuration.TenantId,
Timestamp = DateTime.UtcNow,
EventType = "agent_metrics_collector_stopped",
Severity = EventSeverity.Info,
Source = "MonitoringService",
Phase = EnrollmentPhase.Unknown,
Message = $"AgentSelfMetrics collector stopped after {_collectorIdleTimeoutMinutes} min idle (no real enrollment activity)",
Data = new Dictionary<string, object>
{
{ "reason", "idle_timeout" },
{ "idleTimeoutMinutes", _collectorIdleTimeoutMinutes },
{ "idleMinutes", Math.Round(idleMinutes, 1) }
}
});
}
_collectorsIdleStopped = true;
}
private void RestartPeriodicCollectors()
{
var config = _remoteConfigService?.CurrentConfig;
var collectors = config?.Collectors;
_logger.Info("Restarting periodic collectors — new enrollment activity detected after idle stop");
try
{
var perfInterval = collectors?.PerformanceIntervalSeconds ?? 60;
_performanceCollector = new PerformanceCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_logger,
perfInterval
);
_performanceCollector.Start();
if (collectors?.EnableAgentSelfMetrics != false)
{
var selfMetricsInterval = collectors?.AgentSelfMetricsIntervalSeconds ?? 60;
_agentSelfMetricsCollector = new AgentSelfMetricsCollector(
_configuration.SessionId,
_configuration.TenantId,
_emitEvent,
_apiClient.NetworkMetrics,
_spool,
_logger,
_agentVersion,
selfMetricsInterval
);
_agentSelfMetricsCollector.Start();
}
_lastRealEventTime = DateTime.UtcNow;
_collectorsIdleStopped = false;
if (_collectorIdleTimeoutMinutes > 0)
{
_idleCheckTimer?.Change(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
}
_logger.Info("Periodic collectors restarted successfully");
}
catch (Exception ex)
{
_logger.Error("Error restarting periodic collectors", ex);
}
}
public void Dispose()
{
_idleCheckTimer?.Dispose();
_performanceCollector?.Dispose();
_agentSelfMetricsCollector?.Dispose();
}
}
}
@@ -1 +1 @@
941
945