mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
test(logging): 添加采样过滤器和统计附加器的单元测试
- 为SamplingFilter添加完整的单元测试覆盖采样率、时间窗口、线程安全等功能 - 为StatisticsAppender添加全面的单元测试验证统计计算、错误率跟踪、报告生成功能 - 测试各个日志级别和记录器的独立状态维护 - 验证多线程环境下的数据一致性 - 包含边界条件和异常情况的测试用例
This commit is contained in:
parent
c5ed053f2c
commit
cd407dc93c
133
GFramework.Core.Tests/logging/SamplingFilterTests.cs
Normal file
133
GFramework.Core.Tests/logging/SamplingFilterTests.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using GFramework.Core.Abstractions.logging;
|
||||
using GFramework.Core.logging.filters;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.logging;
|
||||
|
||||
[TestFixture]
|
||||
public class SamplingFilterTests
|
||||
{
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Sample_Logs_By_Rate()
|
||||
{
|
||||
var filter = new SamplingFilter(sampleRate: 3, timeWindow: TimeSpan.FromMinutes(1));
|
||||
var entry = new LogEntry(
|
||||
DateTime.UtcNow,
|
||||
LogLevel.Info,
|
||||
"TestLogger",
|
||||
"Test message",
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 前 3 条:第 1 条通过,第 2、3 条被过滤
|
||||
Assert.That(filter.ShouldLog(entry), Is.True); // 1st
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 2nd
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 3rd
|
||||
|
||||
// 第 4 条通过(新周期)
|
||||
Assert.That(filter.ShouldLog(entry), Is.True); // 4th
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 5th
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 6th
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Reset_After_Time_Window()
|
||||
{
|
||||
var filter = new SamplingFilter(sampleRate: 2, timeWindow: TimeSpan.FromMilliseconds(100));
|
||||
var entry = new LogEntry(
|
||||
DateTime.UtcNow,
|
||||
LogLevel.Info,
|
||||
"TestLogger",
|
||||
"Test message",
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 第一个窗口
|
||||
Assert.That(filter.ShouldLog(entry), Is.True); // 1st
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 2nd
|
||||
|
||||
// 等待时间窗口过期
|
||||
Thread.Sleep(150);
|
||||
|
||||
// 新窗口应该重置计数
|
||||
Assert.That(filter.ShouldLog(entry), Is.True); // 1st in new window
|
||||
Assert.That(filter.ShouldLog(entry), Is.False); // 2nd in new window
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Maintain_Separate_State_Per_Logger()
|
||||
{
|
||||
var filter = new SamplingFilter(sampleRate: 2, timeWindow: TimeSpan.FromMinutes(1));
|
||||
|
||||
var entry1 = new LogEntry(DateTime.UtcNow, LogLevel.Info, "Logger1", "Message", null, null);
|
||||
var entry2 = new LogEntry(DateTime.UtcNow, LogLevel.Info, "Logger2", "Message", null, null);
|
||||
|
||||
// Logger1 的第一条
|
||||
Assert.That(filter.ShouldLog(entry1), Is.True);
|
||||
|
||||
// Logger2 的第一条(独立计数)
|
||||
Assert.That(filter.ShouldLog(entry2), Is.True);
|
||||
|
||||
// Logger1 的第二条
|
||||
Assert.That(filter.ShouldLog(entry1), Is.False);
|
||||
|
||||
// Logger2 的第二条
|
||||
Assert.That(filter.ShouldLog(entry2), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Throw_When_SampleRate_Is_Invalid()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new SamplingFilter(sampleRate: 0, timeWindow: TimeSpan.FromMinutes(1));
|
||||
});
|
||||
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new SamplingFilter(sampleRate: -1, timeWindow: TimeSpan.FromMinutes(1));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Throw_When_TimeWindow_Is_Invalid()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => { new SamplingFilter(sampleRate: 2, timeWindow: TimeSpan.Zero); });
|
||||
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new SamplingFilter(sampleRate: 2, timeWindow: TimeSpan.FromSeconds(-1));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SamplingFilter_Should_Be_Thread_Safe()
|
||||
{
|
||||
var filter = new SamplingFilter(sampleRate: 10, timeWindow: TimeSpan.FromMinutes(1));
|
||||
var entry = new LogEntry(DateTime.UtcNow, LogLevel.Info, "TestLogger", "Message", null, null);
|
||||
|
||||
var passedCount = 0;
|
||||
var tasks = new List<Task>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (int j = 0; j < 100; j++)
|
||||
{
|
||||
if (filter.ShouldLog(entry))
|
||||
{
|
||||
Interlocked.Increment(ref passedCount);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
// 1000 条日志,采样率 10,应该通过约 100 条
|
||||
Assert.That(passedCount, Is.InRange(90, 110));
|
||||
}
|
||||
}
|
||||
186
GFramework.Core.Tests/logging/StatisticsAppenderTests.cs
Normal file
186
GFramework.Core.Tests/logging/StatisticsAppenderTests.cs
Normal file
@ -0,0 +1,186 @@
|
||||
using GFramework.Core.Abstractions.logging;
|
||||
using GFramework.Core.logging.appenders;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.logging;
|
||||
|
||||
[TestFixture]
|
||||
public class StatisticsAppenderTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_appender = new StatisticsAppender();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
}
|
||||
|
||||
private StatisticsAppender _appender = null!;
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Count_Total_Logs()
|
||||
{
|
||||
var entry = CreateLogEntry(LogLevel.Info, "TestLogger", "Message");
|
||||
|
||||
_appender.Append(entry);
|
||||
_appender.Append(entry);
|
||||
_appender.Append(entry);
|
||||
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Count_Errors()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Error, "Logger", "Error"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Fatal, "Logger", "Fatal"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Warning, "Logger", "Warning"));
|
||||
|
||||
Assert.That(_appender.ErrorCount, Is.EqualTo(2)); // Error + Fatal
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Calculate_Error_Rate()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Error, "Logger", "Error"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info"));
|
||||
|
||||
// 1 error out of 4 total = 0.25
|
||||
Assert.That(_appender.ErrorRate, Is.EqualTo(0.25).Within(0.001));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Count_By_Level()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info1"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Info2"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Error, "Logger", "Error"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Warning, "Logger", "Warning"));
|
||||
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Info), Is.EqualTo(2));
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Error), Is.EqualTo(1));
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Warning), Is.EqualTo(1));
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Debug), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Count_By_Logger()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger1", "Message"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger1", "Message"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger2", "Message"));
|
||||
|
||||
Assert.That(_appender.GetCountByLogger("Logger1"), Is.EqualTo(2));
|
||||
Assert.That(_appender.GetCountByLogger("Logger2"), Is.EqualTo(1));
|
||||
Assert.That(_appender.GetCountByLogger("Logger3"), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Track_Uptime()
|
||||
{
|
||||
var startTime = _appender.StartTime;
|
||||
Thread.Sleep(100);
|
||||
|
||||
Assert.That(_appender.Uptime, Is.GreaterThanOrEqualTo(TimeSpan.FromMilliseconds(100)));
|
||||
Assert.That(_appender.StartTime, Is.EqualTo(startTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Reset_Statistics()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger", "Message"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Error, "Logger", "Error"));
|
||||
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(2));
|
||||
Assert.That(_appender.ErrorCount, Is.EqualTo(1));
|
||||
|
||||
var oldStartTime = _appender.StartTime;
|
||||
Thread.Sleep(10);
|
||||
_appender.Reset();
|
||||
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(0));
|
||||
Assert.That(_appender.ErrorCount, Is.EqualTo(0));
|
||||
Assert.That(_appender.GetLevelCounts().Count, Is.EqualTo(0));
|
||||
Assert.That(_appender.GetLoggerCounts().Count, Is.EqualTo(0));
|
||||
Assert.That(_appender.StartTime, Is.GreaterThan(oldStartTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Generate_Report()
|
||||
{
|
||||
_appender.Append(CreateLogEntry(LogLevel.Info, "Logger1", "Info"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Error, "Logger2", "Error"));
|
||||
_appender.Append(CreateLogEntry(LogLevel.Warning, "Logger1", "Warning"));
|
||||
|
||||
var report = _appender.GenerateReport();
|
||||
|
||||
Assert.That(report, Does.Contain("总日志数: 3"));
|
||||
Assert.That(report, Does.Contain("错误日志数: 1"));
|
||||
Assert.That(report, Does.Contain("Info"));
|
||||
Assert.That(report, Does.Contain("Error"));
|
||||
Assert.That(report, Does.Contain("Warning"));
|
||||
Assert.That(report, Does.Contain("Logger1"));
|
||||
Assert.That(report, Does.Contain("Logger2"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Return_Empty_Collections_When_No_Data()
|
||||
{
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(0));
|
||||
Assert.That(_appender.ErrorCount, Is.EqualTo(0));
|
||||
Assert.That(_appender.ErrorRate, Is.EqualTo(0));
|
||||
Assert.That(_appender.GetLevelCounts().Count, Is.EqualTo(0));
|
||||
Assert.That(_appender.GetLoggerCounts().Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StatisticsAppender_Should_Be_Thread_Safe()
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var loggerId = i;
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (int j = 0; j < 100; j++)
|
||||
{
|
||||
var level = j % 2 == 0 ? LogLevel.Info : LogLevel.Error;
|
||||
_appender.Append(CreateLogEntry(level, $"Logger{loggerId}", "Message"));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
Assert.That(_appender.TotalCount, Is.EqualTo(1000));
|
||||
Assert.That(_appender.ErrorCount, Is.EqualTo(500));
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Info), Is.EqualTo(500));
|
||||
Assert.That(_appender.GetCountByLevel(LogLevel.Error), Is.EqualTo(500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Flush_Should_Not_Throw()
|
||||
{
|
||||
Assert.DoesNotThrow(() => _appender.Flush());
|
||||
}
|
||||
|
||||
private static LogEntry CreateLogEntry(LogLevel level, string loggerName, string message)
|
||||
{
|
||||
return new LogEntry(
|
||||
DateTime.UtcNow,
|
||||
level,
|
||||
loggerName,
|
||||
message,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user