- SQLI-WIN-006 is "ikke målbar" (with the forecaster's note) until a volume has enough
history; titles "Drev X er fuldt (om ca. N dage | inden for et døgn)", as SQLI-WIN-005.
- SQLI-WIN-007/008: the startup grace period is "ikke målbar" instead of passed, so
open findings are not resolved and reopened at every restart. One matcher for both
rules (name or display name, prefix*). SQLI-WIN-007 is capped by
Services:MaxFindings (default 15, 1-100) with one summary finding for the rest.
- SQLI-WIN-009: stable order (category, oldest first, key) and one summary finding
over MaxFindings (now bounded 1-100).
- SQLI-WIN-010 only says "uden tegn på at det skete uventet" when the event log was
read past the boot; before that "ikke målbar", and Low "årsagen kan ikke ses" when
the log cannot be read.
- SQLI-WIN-012/013: Tasks:IgnoreUserAndUpdaterTasks (default true) leaves out per-user
and updater tasks; TaskThresholds.Watches is the one filter for the agent and the
rules. A SID is never in a title or subject, only in sensitive evidence.
- SQLI-SYN-001: neutral title "{navn} fejler ({n} gange i træk)", the failure message
only in sensitive evidence; "mindst N" and "... eller tidligere" when every result
given failed; "findes ikke" for a missing service; evidence "Tjenestens tilstand".
- SQLI-SYN-003: "udløber inden for et døgn" / "om 1 dag".
- Agent (minimal): the task filter is TaskThresholds.Watches, and performance samples
get the configured interval.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
133 lines
6.1 KiB
C#
133 lines
6.1 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;
|
|
|
|
/// <summary>
|
|
/// At most this many findings for stopped automatic services per server (SQLI-WIN-007), by name; the rest are named in
|
|
/// one summary finding. Between 1 and 100.
|
|
/// </summary>
|
|
public int MaxFindings { 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; the rest are named in one summary finding. Between 1 and 100.</summary>
|
|
public int MaxFindings { get; set; } = 15;
|
|
|
|
/// <summary>At most this many events per log are read in one collection; the rest is read next time.</summary>
|
|
public int MaxEventsPerCollection { get; set; } = 2000;
|
|
}
|
|
|
|
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>At most this many tasks per server are watched (their run information is read one at a time).</summary>
|
|
public int MaxWatched { get; set; } = 50;
|
|
|
|
/// <summary>
|
|
/// Leave out the tasks programs create per user (a user's SID in the name) or to update themselves (OneDrive, Edge,
|
|
/// Google, User_Feed_Synchronization; <see cref="TaskResults.DefaultIgnoredNames"/>). On by default.
|
|
/// </summary>
|
|
public bool IgnoreUserAndUpdaterTasks { get; set; } = true;
|
|
|
|
/// <summary>Tasks never watched (path\name, or a prefix ending in *).</summary>
|
|
public IList<string> Ignore { get; } = [];
|
|
|
|
/// <summary>Whether the task (its full path) is watched: the agent's filter when it reads tasks, and the rules' too.</summary>
|
|
public bool Watches(string fullName)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(fullName);
|
|
return (IncludeMicrosoftTasks || !TaskResults.IsMicrosoftTask(fullName))
|
|
&& !(IgnoreUserAndUpdaterTasks && TaskResults.IsUserOrUpdaterTask(fullName))
|
|
&& !WindowsServerRules.Matches(fullName, Ignore);
|
|
}
|
|
}
|
|
|
|
/// <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);
|
|
}
|