fix(drivers): overrun-meter tag keys align with the Meter's snake_case convention (driver_type/driver_instance)

This commit is contained in:
Joseph Doherty
2026-08-17 03:54:53 -04:00
parent c149410ba3
commit 97902d6879
8 changed files with 33 additions and 14 deletions
@@ -55,6 +55,25 @@ public static class OtOpcUaTelemetry
Meter.CreateCounter<long>("otopcua.driver.poll.overrun", unit: "{overrun}",
description: "Poll cycles that ran longer than the poll interval (slow success and slow failure both count — see PollGroupEngine.NoteOverrun).");
/// <summary>
/// Tag key carrying the <c>DriverTypeNames</c> constant on <see cref="PollOverruns"/>.
/// <para><b>The snake_case spelling is load-bearing, not a style choice.</b> It is the same
/// literal key <see cref="DriverInstanceLifecycle"/> emits (<c>DriverInstanceActor</c>), and
/// every other instrument on this <see cref="Meter"/> uses bare/snake_case keys
/// (<c>event</c>, <c>kind</c>, <c>outcome</c>, <c>site</c>, <c>reason</c>). A dotted
/// <c>driver.type</c> here would silently split a dashboard's "group by driver type" across
/// two key names that no single query can join. The seven poll drivers reference this const
/// rather than a literal so that can never drift back.</para>
/// </summary>
public const string PollOverrunDriverTypeTag = "driver_type";
/// <summary>
/// Tag key carrying the emitting driver's instance id on <see cref="PollOverruns"/>. Cardinality
/// is bounded by the number of configured driver instances on the node. Snake_case for the same
/// convention reason as <see cref="PollOverrunDriverTypeTag"/>.
/// </summary>
public const string PollOverrunDriverInstanceTag = "driver_instance";
// ---------------- VirtualTag / ScriptedAlarm engines ----------------
public static readonly Counter<long> VirtualTagEval =
@@ -166,8 +166,8 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.AbCip),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.AbCip),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
_alarmProjection = new AbCipAlarmProjection(this, _options.AlarmPollInterval, _logger);
}
@@ -81,8 +81,8 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.AbLegacy),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.AbLegacy),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
/// <summary>Upper bound on the poll-loop failure backoff — adopts the S7-proven 30 s cap fleet-wide (05/STAB-8).</summary>
@@ -126,8 +126,8 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.FOCAS),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.FOCAS),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
/// <summary>True when the supplied DriverConfig JSON carries a real body. The bootstrapper always passes
@@ -192,8 +192,8 @@ public sealed class ModbusDriver
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.Modbus),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.Modbus),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
/// <summary>Upper bound on the poll-loop failure backoff — adopts the S7-proven 30 s cap fleet-wide (05/STAB-8).</summary>
@@ -74,8 +74,8 @@ public sealed class S7Driver
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.S7),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.S7),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
// ---- ISubscribable + IHostConnectivityProbe state ----
@@ -233,8 +233,8 @@ public sealed class SqlDriver
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.Sql),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.Sql),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
/// <summary>
@@ -83,8 +83,8 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// The engine owns no Meter (Core.Abstractions takes no metrics dependency), so this
// closure IS the production consumer of its overrun signal.
onOverrun: _ => OtOpcUaTelemetry.PollOverruns.Add(1,
new KeyValuePair<string, object?>("driver.type", DriverTypeNames.TwinCAT),
new KeyValuePair<string, object?>("driver.instance", driverInstanceId)));
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverTypeTag, DriverTypeNames.TwinCAT),
new KeyValuePair<string, object?>(OtOpcUaTelemetry.PollOverrunDriverInstanceTag, driverInstanceId)));
}
/// <summary>Upper bound on the poll-loop failure backoff — adopts the S7-proven 30 s cap fleet-wide (05/STAB-8).</summary>