5ef27fd9a3
Adds four instruments on the already-allow-listed ZB.MOM.WW.ScadaBridge meter:
scadabridge.deployments.failed{reason}, scadabridge.store_and_forward.activity{action,category},
scadabridge.site.script.runs{outcome} + scadabridge.site.script.duration{outcome} (ms),
scadabridge.site.data_connections.transitions{state}. Queue depth, DCL connection state and
deploy-applied already existed. No new Meter, so ZbTelemetryOptions.Meters needs no change.
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
99 lines
3.9 KiB
C#
99 lines
3.9 KiB
C#
using System.Diagnostics.Metrics;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Observability;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Observability;
|
|
|
|
/// <summary>
|
|
/// ScadaBridge#81: the instruments added beyond the first set. Each is observed through a
|
|
/// <see cref="MeterListener"/> filtered to the emitting (test) thread, because the meter is
|
|
/// process-global and other tests emit on it concurrently.
|
|
/// </summary>
|
|
public sealed class ExtendedInstrumentTests
|
|
{
|
|
private static List<(long Value, Dictionary<string, object?> Tags)> Capture(string instrument, Action act)
|
|
{
|
|
var tid = Environment.CurrentManagedThreadId;
|
|
var got = new List<(long, Dictionary<string, object?>)>();
|
|
using var listener = new MeterListener
|
|
{
|
|
InstrumentPublished = (i, l) =>
|
|
{
|
|
if (i.Meter.Name == ScadaBridgeTelemetry.MeterName && i.Name == instrument)
|
|
l.EnableMeasurementEvents(i);
|
|
}
|
|
};
|
|
listener.SetMeasurementEventCallback<long>((_, m, tags, _) =>
|
|
{
|
|
if (Environment.CurrentManagedThreadId != tid) return;
|
|
var d = new Dictionary<string, object?>();
|
|
foreach (var t in tags) d[t.Key] = t.Value;
|
|
got.Add((m, d));
|
|
});
|
|
listener.Start();
|
|
act();
|
|
return got;
|
|
}
|
|
|
|
/// <summary>A state change emits one transition tagged with the new state; repeating the same state emits none.</summary>
|
|
[Fact]
|
|
public void DataConnectionTransitions_EmitOnlyOnRealStateChange()
|
|
{
|
|
var name = "xfer-" + Guid.NewGuid().ToString("N");
|
|
try
|
|
{
|
|
var got = Capture("scadabridge.site.data_connections.transitions", () =>
|
|
{
|
|
ScadaBridgeTelemetry.RecordDataConnectionState(name, ConnectionHealth.Connecting);
|
|
ScadaBridgeTelemetry.RecordDataConnectionState(name, ConnectionHealth.Connected);
|
|
ScadaBridgeTelemetry.RecordDataConnectionState(name, ConnectionHealth.Connected);
|
|
ScadaBridgeTelemetry.RecordDataConnectionState(name, ConnectionHealth.Disconnected);
|
|
});
|
|
|
|
Assert.Equal(
|
|
new object?[] { "Connecting", "Connected", "Disconnected" },
|
|
got.Select(g => g.Tags["state"]).ToArray());
|
|
}
|
|
finally { ScadaBridgeTelemetry.RemoveDataConnection(name); }
|
|
}
|
|
|
|
/// <summary>Store-and-forward activity is counted by action and category.</summary>
|
|
[Fact]
|
|
public void StoreAndForwardActivity_IsCountedByActionAndCategory()
|
|
{
|
|
var got = Capture("scadabridge.store_and_forward.activity",
|
|
() => ScadaBridgeTelemetry.RecordStoreAndForwardActivity("Parked", "ExternalSystem"));
|
|
|
|
var m = Assert.Single(got);
|
|
Assert.Equal(1, m.Value);
|
|
Assert.Equal("Parked", m.Tags["action"]);
|
|
Assert.Equal("ExternalSystem", m.Tags["category"]);
|
|
}
|
|
|
|
/// <summary>Deployment failures are counted by reason.</summary>
|
|
[Fact]
|
|
public void DeploymentsFailed_IsCountedByReason()
|
|
{
|
|
var got = Capture("scadabridge.deployments.failed",
|
|
() => ScadaBridgeTelemetry.RecordDeploymentFailed("timeout"));
|
|
|
|
var m = Assert.Single(got);
|
|
Assert.Equal("timeout", m.Tags["reason"]);
|
|
}
|
|
|
|
/// <summary>A script run emits both the run counter and the duration histogram, clamping negatives.</summary>
|
|
[Fact]
|
|
public void ScriptRun_EmitsCounterAndDuration()
|
|
{
|
|
var runs = Capture("scadabridge.site.script.runs",
|
|
() => ScadaBridgeTelemetry.RecordScriptRun("failed", 5));
|
|
Assert.Equal("failed", Assert.Single(runs).Tags["outcome"]);
|
|
|
|
var durations = Capture("scadabridge.site.script.duration",
|
|
() => ScadaBridgeTelemetry.RecordScriptRun("completed", -3));
|
|
var d = Assert.Single(durations);
|
|
Assert.Equal(0, d.Value);
|
|
Assert.Equal("completed", d.Tags["outcome"]);
|
|
}
|
|
}
|