Review 1.2: read only the default trace's current file between rollovers
With ALTER TRACE the error log collector read every default trace file (up to 5 x 20 MB) every 5 minutes. While the current file is the one read last time, everything since then is in it, so only that file is read; after a rollover (and the first time) all files are read once as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8e551bcaa6
commit
d9e3b4ee6d
2 changed files with 16 additions and 6 deletions
|
|
@ -69,13 +69,14 @@ public sealed class ErrorLogCollector(int loginFailureBurst = 20) : Collector<Er
|
||||||
List<ErrorLogEntry> entries;
|
List<ErrorLogEntry> entries;
|
||||||
ErrorLogSource source;
|
ErrorLogSource source;
|
||||||
string? coverage = null;
|
string? coverage = null;
|
||||||
|
string? traceFile = null;
|
||||||
if (errorLog && await ReadErrorLogAsync(connection, since, now, offset, ct).ConfigureAwait(false) is { } fromLog)
|
if (errorLog && await ReadErrorLogAsync(connection, since, now, offset, ct).ConfigureAwait(false) is { } fromLog)
|
||||||
{
|
{
|
||||||
(entries, source) = (fromLog, ErrorLogSource.ErrorLog);
|
(entries, source) = (fromLog, ErrorLogSource.ErrorLog);
|
||||||
}
|
}
|
||||||
else if (await ReadDefaultTraceAsync(connection, since, offset, ct).ConfigureAwait(false) is { } fromTrace)
|
else if (await ReadDefaultTraceAsync(connection, since, offset, previous?.TraceFile, ct).ConfigureAwait(false) is { } fromTrace)
|
||||||
{
|
{
|
||||||
(entries, source, coverage) = (fromTrace, ErrorLogSource.DefaultTrace, DefaultTraceCoverage);
|
(entries, source, coverage, traceFile) = (fromTrace.Entries, ErrorLogSource.DefaultTrace, DefaultTraceCoverage, fromTrace.File);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -103,6 +104,7 @@ public sealed class ErrorLogCollector(int loginFailureBurst = 20) : Collector<Er
|
||||||
Groups = groups,
|
Groups = groups,
|
||||||
Recent = ErrorLogAnalysis.Recent(previous?.Recent ?? [], groups, context.Now),
|
Recent = ErrorLogAnalysis.Recent(previous?.Recent ?? [], groups, context.Now),
|
||||||
Coverage = coverage,
|
Coverage = coverage,
|
||||||
|
TraceFile = traceFile,
|
||||||
LoginFailureBurst = loginFailureBurst,
|
LoginFailureBurst = loginFailureBurst,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -126,8 +128,8 @@ public sealed class ErrorLogCollector(int loginFailureBurst = 20) : Collector<Er
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>ErrorLog (22) and Audit Login Failed (20) events from the default trace, or null without ALTER TRACE.</summary>
|
/// <summary>ErrorLog (22) and Audit Login Failed (20) events from the default trace, or null without ALTER TRACE.</summary>
|
||||||
private static async Task<List<ErrorLogEntry>?> ReadDefaultTraceAsync(DbConnection connection, DateTimeOffset since, TimeSpan offset,
|
private static async Task<(List<ErrorLogEntry> Entries, string File)?> ReadDefaultTraceAsync(DbConnection connection, DateTimeOffset since, TimeSpan offset,
|
||||||
CancellationToken cancellationToken)
|
string? previousFile, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -138,9 +140,14 @@ public sealed class ErrorLogCollector(int loginFailureBurst = 20) : Collector<Er
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All rollover files (up to 100 MB) every 5 minutes is not cheap. While the current file is the one read last
|
||||||
|
// time, everything since then is in it; after a rollover (and the first time) all files are read once.
|
||||||
|
var files = string.Equals(path, previousFile, StringComparison.OrdinalIgnoreCase)
|
||||||
|
? Db.Literal(path) + ", 1"
|
||||||
|
: Db.Literal(PrivilegeAnalysis.TraceFileBase(path)) + ", DEFAULT";
|
||||||
var sql = $"""
|
var sql = $"""
|
||||||
SELECT StartTime, EventClass, Error, Severity, CAST(TextData AS nvarchar(2000))
|
SELECT StartTime, EventClass, Error, Severity, CAST(TextData AS nvarchar(2000))
|
||||||
FROM sys.fn_trace_gettable({Db.Literal(PrivilegeAnalysis.TraceFileBase(path))}, DEFAULT)
|
FROM sys.fn_trace_gettable({files})
|
||||||
WHERE EventClass IN (20, 22) AND StartTime > '{Local(since, offset)}'
|
WHERE EventClass IN (20, 22) AND StartTime > '{Local(since, offset)}'
|
||||||
""";
|
""";
|
||||||
var rows = await Db.QueryAsync(connection, sql, r => (At: Utc(r.DateTime(0)) - offset, Class: r.Int(1), Error: r.NullableInt(2), Severity: r.NullableInt(3),
|
var rows = await Db.QueryAsync(connection, sql, r => (At: Utc(r.DateTime(0)) - offset, Class: r.Int(1), Error: r.NullableInt(2), Severity: r.NullableInt(3),
|
||||||
|
|
@ -164,7 +171,7 @@ public sealed class ErrorLogCollector(int loginFailureBurst = 20) : Collector<Er
|
||||||
|
|
||||||
// StartTime is compared to whole seconds above, so the entries of that second that were read last time
|
// StartTime is compared to whole seconds above, so the entries of that second that were read last time
|
||||||
// (the newest of them is ReadUntil) come back; leave them out, or they are counted again every collection.
|
// (the newest of them is ReadUntil) come back; leave them out, or they are counted again every collection.
|
||||||
return [.. entries.Where(e => e.At > since)];
|
return ([.. entries.Where(e => e.At > since)], path);
|
||||||
}
|
}
|
||||||
catch (DbException)
|
catch (DbException)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ public sealed record ErrorLogSnapshot
|
||||||
/// <summary>The newest entry read; the next collection continues after it.</summary>
|
/// <summary>The newest entry read; the next collection continues after it.</summary>
|
||||||
public required DateTimeOffset ReadUntil { get; init; }
|
public required DateTimeOffset ReadUntil { get; init; }
|
||||||
|
|
||||||
|
/// <summary>The default trace's current file when it was read; while it stays the same, only that file is read next time.</summary>
|
||||||
|
public string? TraceFile { get; init; }
|
||||||
|
|
||||||
/// <summary>What was found since the previous collection, grouped.</summary>
|
/// <summary>What was found since the previous collection, grouped.</summary>
|
||||||
public IReadOnlyList<ErrorLogGroup> Groups { get; init; } = [];
|
public IReadOnlyList<ErrorLogGroup> Groups { get; init; } = [];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue