feat(health): wire ISiteEventLogger.FailedWriteCount into SiteHealthReport (#30, M2.16)
Add SiteHealthReport.SiteEventLogWriteFailures (trailing optional long = 0, additive-only), ISiteHealthCollector.SetSiteEventLogWriteFailures (default no-op so existing fakes compile), and SiteEventLogFailureCountReporter (hosted service in HealthMonitoring, Func<long> delegate to avoid the HealthMonitoring → StoreAndForward → SiteEventLogging cycle). Registration helper AddSiteEventLogHealthMetricsBridge added to HealthMonitoring.ServiceCollectionExtensions; wired in SiteServiceRegistration after AddSiteEventLogging. Tests: SiteEventLogWriteFailuresMetricTests (4 collector tests) + SiteEventLogFailureCountReporterTests (2 poller tests) in HealthMonitoring.Tests. 79/79 HealthMonitoring.Tests green, 59/59 SiteEventLogging.Tests green, 0 warnings.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.HealthMonitoring;
|
||||
@@ -50,6 +52,68 @@ public static class ServiceCollectionExtensions
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Site Event Logging (#12) M2.16 (#30) — register the
|
||||
/// <see cref="SiteEventLogFailureCountReporter"/> hosted service that
|
||||
/// periodically reads the cumulative event-log write-failure count and
|
||||
/// pushes it into <see cref="ISiteHealthCollector"/> as a point-in-time
|
||||
/// snapshot (<c>SiteEventLogWriteFailures</c> on the site health report).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Must be called AFTER <see cref="AddSiteHealthMonitoring"/> (or
|
||||
/// <see cref="AddHealthMonitoring"/>) which registers the
|
||||
/// <see cref="ISiteHealthCollector"/> the reporter depends on.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Why a Func<long> delegate instead of ISiteEventLogger.</b>
|
||||
/// <c>HealthMonitoring</c> must not reference <c>SiteEventLogging</c> directly —
|
||||
/// the <c>StoreAndForward → SiteEventLogging</c> edge already exists in the
|
||||
/// transitive graph, and <c>HealthMonitoring → StoreAndForward</c> is an
|
||||
/// existing direct reference; adding <c>HealthMonitoring → SiteEventLogging</c>
|
||||
/// would complete a cycle. The <see cref="Func{TResult}"/> delegate seam keeps
|
||||
/// the dependency acyclic: the caller (Host site wiring) captures
|
||||
/// <c>ISiteEventLogger.FailedWriteCount</c> as a lambda and passes it here.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Idempotent — a sentinel check on the
|
||||
/// <see cref="SiteEventLogFailureCountReporter"/> hosted-service descriptor
|
||||
/// short-circuits subsequent calls so the hosted service is not
|
||||
/// double-registered (AddHostedService has no TryAdd variant).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="services">The service collection to register into.</param>
|
||||
/// <param name="failedWriteCountProvider">
|
||||
/// A factory delegate that, given the root <see cref="IServiceProvider"/>,
|
||||
/// returns a <see cref="Func{TResult}"/> that reads the current cumulative
|
||||
/// event-log write-failure count. Typically:
|
||||
/// <c>sp => () => sp.GetRequiredService<ISiteEventLogger>().FailedWriteCount</c>.
|
||||
/// The factory is evaluated once at hosted-service resolution time; the inner
|
||||
/// <see cref="Func{TResult}"/> is called on every poll tick.
|
||||
/// </param>
|
||||
/// <returns>The same <see cref="IServiceCollection"/> for chaining.</returns>
|
||||
public static IServiceCollection AddSiteEventLogHealthMetricsBridge(
|
||||
this IServiceCollection services,
|
||||
Func<IServiceProvider, Func<long>> failedWriteCountProvider)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(failedWriteCountProvider);
|
||||
|
||||
// Idempotent guard — mirrors AddAuditLogHealthMetricsBridge's
|
||||
// SiteAuditBacklogReporter sentinel check.
|
||||
if (services.Any(d => d.ImplementationType == typeof(SiteEventLogFailureCountReporter)))
|
||||
{
|
||||
return services;
|
||||
}
|
||||
|
||||
services.AddHostedService(sp => new SiteEventLogFailureCountReporter(
|
||||
failedWriteCountProvider(sp),
|
||||
sp.GetRequiredService<ISiteHealthCollector>(),
|
||||
sp.GetRequiredService<ILogger<SiteEventLogFailureCountReporter>>()));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HealthMonitoring-014: register the <see cref="HealthMonitoringOptionsValidator"/>
|
||||
/// so a misconfigured <c>ScadaBridge:HealthMonitoring</c> section (zero/negative
|
||||
|
||||
Reference in New Issue
Block a user