Some checks failed
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
OtOpcUaTelemetry (Commons/Observability) centralizes the project's Meter + ActivitySource so all instrumentation points emit through a single named surface. Counters cover the hot paths: otopcua.deploy.applied (outcome=ack|reject) otopcua.deploy.apply.duration (s, histogram) otopcua.driver.lifecycle (event=spawn|spawn_stub|stop|fault) otopcua.virtualtag.eval (outcome=ok|fail|skip) otopcua.scriptedalarm.transition (state=activated|acknowledged|cleared) otopcua.opcua.sink.write (kind=value|alarm|rebuild) otopcua.redundancy.service_level_change (level=byte) Plus two ActivitySource spans: otopcua.deploy.apply wraps DriverHostActor.ApplyAndAck otopcua.opcua.address_space_rebuild wraps OpcUaPublishActor.HandleRebuild Instruments are no-op until a listener attaches, so tests + dev hosts pay nothing for unread telemetry. Host Program.cs gains AddOtOpcUaObservability() (binds the OtOpcUa Meter + ActivitySource to OpenTelemetry, attaches a Prometheus exporter) and MapOtOpcUaMetrics() (mounts /metrics scrape endpoint). Driver-side internals + ASP.NET request metrics deliberately stay off — the scrape payload is scoped to OtOpcUa signals only. Tests use MeterListener + ActivityListener to verify VirtualTagActor.eval, OpcUaPublishActor.AttributeValueUpdate, and RebuildAddressSpace actually emit on the central instruments. Runtime suite is 72 / 72 green (+3). Closes #105. Path A (F13b/c/d) complete; next batch options: #85 UNS folder hierarchy in SDK, or F8b/F9b production engine bindings.
82 lines
4.3 KiB
C#
82 lines
4.3 KiB
C#
using System.Diagnostics;
|
|
using System.Diagnostics.Metrics;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Commons.Observability;
|
|
|
|
/// <summary>
|
|
/// Central <see cref="Meter"/> + <see cref="ActivitySource"/> definitions for OtOpcUa.
|
|
/// All Akka actors, the OPC UA publish path, and the deploy coordinator emit through these
|
|
/// pre-created instruments so a single OpenTelemetry / Prometheus binding in <c>Host</c>
|
|
/// catches everything. No exporter is required — instruments are no-op until a listener
|
|
/// attaches, so tests and dev hosts pay nothing for instrumentation that nobody scrapes.
|
|
///
|
|
/// Instrument names follow the OpenTelemetry semantic convention pattern
|
|
/// <c>otopcua.<subsystem>.<event></c>. Subsystem is one of: deploy, driver,
|
|
/// virtualtag, scriptedalarm, opcua, redundancy.
|
|
/// </summary>
|
|
public static class OtOpcUaTelemetry
|
|
{
|
|
public const string MeterName = "ZB.MOM.WW.OtOpcUa";
|
|
public const string ActivitySourceName = "ZB.MOM.WW.OtOpcUa";
|
|
|
|
/// <summary>Singleton <see cref="Meter"/> all counters/histograms hang off.</summary>
|
|
public static readonly Meter Meter = new(MeterName);
|
|
|
|
/// <summary>Singleton <see cref="ActivitySource"/> used to start spans wrapping deploy/apply/rebuild.</summary>
|
|
public static readonly ActivitySource ActivitySource = new(ActivitySourceName);
|
|
|
|
// ---------------- Deployment / driver-host coordination ----------------
|
|
|
|
/// <summary>Incremented every time DriverHostActor finishes applying a deployment (Ack or Reject).</summary>
|
|
public static readonly Counter<long> DeploymentApplied =
|
|
Meter.CreateCounter<long>("otopcua.deploy.applied", unit: "{deployment}",
|
|
description: "Deployments applied by a driver-role node (outcome=ack|reject).");
|
|
|
|
/// <summary>Time from DriverHostActor receiving DispatchDeployment to emitting the ack/reject.</summary>
|
|
public static readonly Histogram<double> DeploymentApplyDurationSec =
|
|
Meter.CreateHistogram<double>("otopcua.deploy.apply.duration", unit: "s",
|
|
description: "Driver-role apply latency from DispatchDeployment → Ack/Reject.");
|
|
|
|
/// <summary>DriverInstanceActor spawn count (added=new instance; stop=disposed).</summary>
|
|
public static readonly Counter<long> DriverInstanceLifecycle =
|
|
Meter.CreateCounter<long>("otopcua.driver.lifecycle", unit: "{event}",
|
|
description: "DriverInstanceActor lifecycle transitions (event=spawn|stop|fault).");
|
|
|
|
// ---------------- VirtualTag / ScriptedAlarm engines ----------------
|
|
|
|
public static readonly Counter<long> VirtualTagEval =
|
|
Meter.CreateCounter<long>("otopcua.virtualtag.eval", unit: "{eval}",
|
|
description: "Virtual-tag evaluations attempted (outcome=ok|fail|skip).");
|
|
|
|
public static readonly Counter<long> ScriptedAlarmTransition =
|
|
Meter.CreateCounter<long>("otopcua.scriptedalarm.transition", unit: "{transition}",
|
|
description: "Scripted-alarm state transitions (state=active|acknowledged|inactive).");
|
|
|
|
// ---------------- OPC UA address-space + redundancy ----------------
|
|
|
|
public static readonly Counter<long> OpcUaSinkWrite =
|
|
Meter.CreateCounter<long>("otopcua.opcua.sink.write", unit: "{write}",
|
|
description: "Writes that landed in IOpcUaAddressSpaceSink (kind=value|alarm|rebuild).");
|
|
|
|
public static readonly Counter<long> ServiceLevelChange =
|
|
Meter.CreateCounter<long>("otopcua.redundancy.service_level_change", unit: "{change}",
|
|
description: "OPC UA Server.ServiceLevel transitions emitted by the redundancy state.");
|
|
|
|
// ---------------- Convenience helpers ----------------
|
|
|
|
/// <summary>
|
|
/// Starts a deploy span tagged with the deployment id. Caller disposes to close. Returns
|
|
/// null when no listener is attached so the call site stays cheap on undecorated builds.
|
|
/// </summary>
|
|
public static Activity? StartDeployApplySpan(string deploymentId)
|
|
{
|
|
var activity = ActivitySource.StartActivity("otopcua.deploy.apply", ActivityKind.Internal);
|
|
activity?.SetTag("otopcua.deployment_id", deploymentId);
|
|
return activity;
|
|
}
|
|
|
|
/// <summary>Span wrapping a full OPC UA address-space rebuild (Phase7 plan → apply).</summary>
|
|
public static Activity? StartAddressSpaceRebuildSpan()
|
|
=> ActivitySource.StartActivity("otopcua.opcua.address_space_rebuild", ActivityKind.Internal);
|
|
}
|