sqlinsight-pro/src/SQLInsight.Rules/Servers/ServerRuleSettings.cs
Søren Eggert Lundsteen Olsen ba7c751b5c Rules: Windows servers (SQLI-WIN-001..013) and synthetic checks (SQLI-SYN-001..003)
Scoped rules with their own context (a Windows server, a synthetic check) that
produce the same findings and outcomes as the instance rules; missing data is
"ikke målbar" with the collector's reason. Thresholds are settings
(SQLInsight:Servers, SQLInsight:SyntheticChecks).

- Sustained CPU, low memory (share and MB), paging with low memory, commit charge.
- Free disk space and "fuld om ca. N dage" forecasts.
- Stopped automatic services (minus those that stop by design, and a grace
  period after a restart) and watched services that must run.
- Windows event log groups: important kinds at once, ordinary errors when they
  repeat, capped per server; sample messages are sensitive.
- Unexpected restart (Kernel-Power 41, EventLog 6008, BugCheck), optional uptime.
- Scheduled tasks that failed or did not run as planned.
- Synthetic checks failing (twice in a row), "svarer ikke, selv om tjenesten
  kører", slow answers, TLS certificates about to expire.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:58:18 +02:00

106 lines
4.6 KiB
C#

using SQLInsight.Core.Collection;
using SQLInsight.Core.Servers;
namespace SQLInsight.Rules.Servers;
/// <summary>
/// Thresholds of the Windows server rules (SQLInsight:Servers in the agent's appsettings.json). Not sealed: the agent's
/// options add how and how often to collect.
/// </summary>
public class ServerRuleSettings
{
public CpuThresholds Cpu { get; set; } = new();
public MemoryThresholds Memory { get; set; } = new();
public DiskThresholds Disks { get; set; } = new();
public ServiceThresholds Services { get; set; } = new();
public EventLogThresholds EventLog { get; set; } = new();
public TaskThresholds Tasks { get; set; } = new();
/// <summary>A server not restarted for more than this many days is a (low) finding: it most likely lacks Windows updates. 0: off.</summary>
public int UptimeDays { get; set; }
}
public sealed class CpuThresholds
{
/// <summary>Average CPU at or above this (percent) …</summary>
public double HighPercent { get; set; } = 90;
/// <summary>… for this many minutes is a finding (SQLI-WIN-001).</summary>
public int SustainedMinutes { get; set; } = 15;
}
public sealed class MemoryThresholds
{
/// <summary>Available memory below this share (percent) …</summary>
public double AvailablePercent { get; set; } = 5;
/// <summary>… and below this many MB is a finding (SQLI-WIN-002). Both, so big SQL Server hosts with little left by design are not flagged.</summary>
public double AvailableMegabytes { get; set; } = 1024;
/// <summary>Hard page faults per second above this, while memory is low, is paging (SQLI-WIN-003).</summary>
public double PagesPerSecond { get; set; } = 1000;
/// <summary>Committed memory at or above this share of the commit limit (percent) is a finding (SQLI-WIN-004).</summary>
public double CommitPercent { get; set; } = 90;
}
public sealed class DiskThresholds
{
/// <summary>Free space below this (percent) is a finding (SQLI-WIN-005) …</summary>
public double FreePercent { get; set; } = 10;
/// <summary>… High below this …</summary>
public double CriticalFreePercent { get; set; } = 5;
/// <summary>… unless there is at least this much free (GB): a big volume with a small share free can be fine.</summary>
public double EnoughFreeGigabytes { get; set; } = 50;
/// <summary>A volume forecast to be full within this many days is a finding (SQLI-WIN-006); within 7 days High, 2 days Critical.</summary>
public int ForecastDays { get; set; } = 30;
/// <summary>How many days of SQLInsight's own samples the forecast is computed from.</summary>
public int ForecastHistoryDays { get; set; } = 14;
}
public sealed class ServiceThresholds
{
/// <summary>
/// Services that may be stopped although they start automatically (they stop by design), in addition to the
/// built-in list. Name, or a prefix ending in *.
/// </summary>
public IList<string> Ignore { get; } = [];
/// <summary>Right after a restart services are still starting: no finding for this many minutes.</summary>
public int StartupGraceMinutes { get; set; } = 15;
}
public sealed class EventLogThresholds
{
/// <summary>An ordinary error must repeat this many times in 24 hours before it is a finding; the important kinds count at once.</summary>
public int RepeatThreshold { get; set; } = 5;
/// <summary>Events left out as noise, in addition to the built-in list: "Kilde:id", "Kilde" or "Kilde*".</summary>
public IList<string> Ignore { get; } = [];
/// <summary>At most this many event findings per server, most important first.</summary>
public int MaxFindings { get; set; } = 15;
}
public sealed class TaskThresholds
{
/// <summary>Also watch Microsoft's own tasks (\Microsoft\…). Off: there are hundreds, and their failures are rarely yours.</summary>
public bool IncludeMicrosoftTasks { get; set; }
/// <summary>A task whose next run time is more than this many minutes in the past has not run as planned (SQLI-WIN-013).</summary>
public int LateMinutes { get; set; } = 60;
/// <summary>Tasks never watched (path\name, or a prefix ending in *).</summary>
public IList<string> Ignore { get; } = [];
}
/// <summary>What the Windows server rules see: the server, the latest data per area, and the thresholds.</summary>
public sealed record ServerRuleContext(ServerIdentity Server, DateTimeOffset Now, CollectedData Data, ServerRuleSettings Settings)
{
/// <summary>The interval performance samples are taken at, for "sustained" averages.</summary>
public TimeSpan PerformanceInterval { get; init; } = ServerArea.DefaultInterval(ServerArea.Performance);
}