Files
process-monitor/Services/PerformanceMonitoringService.cs

167 lines
5.2 KiB
C#

using System.Diagnostics;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MarketAlly.ProcessMonitor.Models;
namespace MarketAlly.ProcessMonitor.Services;
/// <summary>
/// Monitors system and process performance metrics
/// </summary>
public class PerformanceMonitoringService : BackgroundService
{
private readonly ILogger<PerformanceMonitoringService> _logger;
private readonly AppSettings _appSettings;
private readonly PerformanceCounter? _cpuCounter;
private readonly PerformanceCounter? _memoryCounter;
private readonly Dictionary<string, ProcessPerformanceInfo> _processMetrics = new();
public PerformanceMonitoringService(
ILogger<PerformanceMonitoringService> logger,
AppSettings appSettings)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_appSettings = appSettings ?? throw new ArgumentNullException(nameof(appSettings));
if (OperatingSystem.IsWindows() && _appSettings.EnablePerformanceMonitoring)
{
try
{
_cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
_memoryCounter = new PerformanceCounter("Memory", "Available MBytes");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to initialize performance counters");
}
}
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!_appSettings.EnablePerformanceMonitoring)
{
_logger.LogInformation("Performance monitoring is disabled");
return;
}
while (!stoppingToken.IsCancellationRequested)
{
try
{
await CollectMetricsAsync();
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error collecting performance metrics");
}
}
}
private async Task CollectMetricsAsync()
{
try
{
var systemMetrics = GetSystemMetrics();
_logger.LogInformation("System metrics - CPU: {Cpu:F1}%, Memory Available: {Memory}MB",
systemMetrics.CpuUsage, systemMetrics.AvailableMemoryMB);
// Collect process-specific metrics
var processes = Process.GetProcesses();
foreach (var process in processes)
{
try
{
if (_processMetrics.ContainsKey(process.ProcessName))
{
var metrics = GetProcessMetrics(process);
_processMetrics[process.ProcessName] = metrics;
if (metrics.WorkingSetMB > 1000) // Log if process uses more than 1GB
{
_logger.LogWarning("Process {ProcessName} is using {Memory}MB of memory",
process.ProcessName, metrics.WorkingSetMB);
}
}
}
catch
{
// Ignore individual process errors
}
}
await Task.CompletedTask;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to collect metrics");
}
}
private SystemMetrics GetSystemMetrics()
{
var metrics = new SystemMetrics();
if (OperatingSystem.IsWindows() && _cpuCounter != null && _memoryCounter != null)
{
try
{
metrics.CpuUsage = _cpuCounter.NextValue();
metrics.AvailableMemoryMB = (long)_memoryCounter.NextValue();
}
catch { }
}
else
{
// Cross-platform fallback
using var process = Process.GetCurrentProcess();
metrics.AvailableMemoryMB = GC.GetTotalMemory(false) / (1024 * 1024);
}
return metrics;
}
private ProcessPerformanceInfo GetProcessMetrics(Process process)
{
return new ProcessPerformanceInfo
{
ProcessName = process.ProcessName,
ProcessId = process.Id,
WorkingSetMB = process.WorkingSet64 / (1024 * 1024),
ThreadCount = process.Threads.Count,
HandleCount = process.HandleCount
};
}
public Dictionary<string, ProcessPerformanceInfo> GetCurrentMetrics()
{
return new Dictionary<string, ProcessPerformanceInfo>(_processMetrics);
}
public override void Dispose()
{
_cpuCounter?.Dispose();
_memoryCounter?.Dispose();
base.Dispose();
}
}
public class SystemMetrics
{
public double CpuUsage { get; set; }
public long AvailableMemoryMB { get; set; }
}
public class ProcessPerformanceInfo
{
public string ProcessName { get; set; } = string.Empty;
public int ProcessId { get; set; }
public long WorkingSetMB { get; set; }
public int ThreadCount { get; set; }
public int HandleCount { get; set; }
}