mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 05:00:07 +00:00
agent resilliency
This commit is contained in:
@@ -19,6 +19,7 @@ namespace AutopilotMonitor.Agent.Core.Configuration
|
||||
private readonly string _tenantId;
|
||||
private readonly AgentLogger _logger;
|
||||
private readonly string _cacheFilePath;
|
||||
private readonly EmergencyReporter _emergencyReporter;
|
||||
|
||||
private AgentConfigResponse _currentConfig;
|
||||
private readonly object _configLock = new object();
|
||||
@@ -37,11 +38,12 @@ namespace AutopilotMonitor.Agent.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteConfigService(BackendApiClient apiClient, string tenantId, AgentLogger logger)
|
||||
public RemoteConfigService(BackendApiClient apiClient, string tenantId, AgentLogger logger, EmergencyReporter emergencyReporter = null)
|
||||
{
|
||||
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
_tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_emergencyReporter = emergencyReporter;
|
||||
|
||||
var cacheDir = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Config");
|
||||
_cacheFilePath = Path.Combine(cacheDir, "remote-config.json");
|
||||
@@ -74,6 +76,14 @@ namespace AutopilotMonitor.Agent.Core.Configuration
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warning($"Failed to fetch remote config: {ex.Message}");
|
||||
|
||||
// Report to emergency channel if available (not available in --run-gather-rules mode)
|
||||
if (_emergencyReporter != null)
|
||||
{
|
||||
_ = _emergencyReporter.TrySendAsync(
|
||||
AgentErrorType.ConfigFetchFailed,
|
||||
ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to cached config
|
||||
|
||||
@@ -304,8 +304,17 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
lastErrorCode = $"HTTP {(int)response.StatusCode} {response.ReasonPhrase}";
|
||||
var statusCode = (int)response.StatusCode;
|
||||
lastErrorCode = $"HTTP {statusCode} {response.ReasonPhrase}";
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Auth errors (401/403) are permanent — SAS token invalid or expired, retrying won't help
|
||||
if (statusCode == 401 || statusCode == 403)
|
||||
{
|
||||
_logger.Warning($"Blob upload auth error (not retryable): {lastErrorCode} - {responseBody}");
|
||||
return (false, lastErrorCode);
|
||||
}
|
||||
|
||||
_logger.Warning($"Blob upload attempt {attempt} failed: {lastErrorCode} - {responseBody}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
_remoteConfigService = new RemoteConfigService(_apiClient, _configuration.TenantId, _logger);
|
||||
_remoteConfigService = new RemoteConfigService(_apiClient, _configuration.TenantId, _logger, _emergencyReporter);
|
||||
_remoteConfigService.FetchConfigAsync().Wait(TimeSpan.FromSeconds(15));
|
||||
ApplyRuntimeSettingsFromRemoteConfig();
|
||||
}
|
||||
@@ -546,6 +546,12 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error("Failed to register session", ex);
|
||||
|
||||
// Report to emergency channel — one-shot, no threshold needed.
|
||||
// EmergencyReporter dedup ensures RegisterSessionFailed:null sent only once per session.
|
||||
_ = _emergencyReporter.TrySendAsync(
|
||||
AgentErrorType.RegisterSessionFailed,
|
||||
ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,8 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Network
|
||||
var ndjson = CreateNdjson(request);
|
||||
var compressedContent = CompressWithGzip(ndjson);
|
||||
|
||||
_logger?.Debug($"IngestEventsAsync: POST {url} — {request.Events.Count} events, {compressedContent.Length} bytes compressed");
|
||||
|
||||
var httpRequest = new HttpRequestMessage(HttpMethod.Post, url)
|
||||
{
|
||||
Content = new ByteArrayContent(compressedContent)
|
||||
@@ -165,6 +167,8 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Network
|
||||
var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
AddSecurityHeaders(httpRequest);
|
||||
|
||||
_logger?.Debug($"GetAgentConfigAsync: GET {url}");
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var failed = false;
|
||||
long bytesDown = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Network
|
||||
///
|
||||
/// A failure in this reporter is silently swallowed — it must never cascade into the main loop.
|
||||
/// </summary>
|
||||
internal class EmergencyReporter
|
||||
public class EmergencyReporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of consecutive upload failures required before the first emergency report is sent.
|
||||
|
||||
@@ -1 +1 @@
|
||||
159
|
||||
166
|
||||
|
||||
Reference in New Issue
Block a user