From 4e0d8ccfed02f0b33a6183ae47c89da14df62257 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 16:34:46 -0400 Subject: [PATCH 1/4] chore(mxgateway): gitignore CommentChecker doc-review artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index f0b93db..7c0409d 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,8 @@ generated-scratch/ # Keep empty directories with .gitkeep files when needed !.gitkeep + +# Documentation review artifacts (CommentChecker output) +*-docs-issues.md +*-docs-fixed.md +*-docs-final.md From abb2cfb84bcab4b979ffbe10e4808afb67ae749b Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 16:39:56 -0400 Subject: [PATCH 2/4] =?UTF-8?q?feat(mxgateway):=20normalize=20metrics=20?= =?UTF-8?q?=E2=80=94=20meter=20ZB.MOM.WW.MxGateway=20+=20histograms=20in?= =?UTF-8?q?=20seconds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Metrics/GatewayMetrics.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs b/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs index 65d0c16..8727478 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs @@ -5,7 +5,7 @@ namespace ZB.MOM.WW.MxGateway.Server.Metrics; public sealed class GatewayMetrics : IDisposable { - public const string MeterName = "MxGateway.Server"; + public const string MeterName = "ZB.MOM.WW.MxGateway"; private readonly object _syncRoot = new(); private readonly Meter _meter; @@ -68,9 +68,9 @@ public sealed class GatewayMetrics : IDisposable _heartbeatFailuresCounter = _meter.CreateCounter("mxgateway.heartbeats.failed"); _streamDisconnectsCounter = _meter.CreateCounter("mxgateway.grpc.streams.disconnected"); _retryAttemptsCounter = _meter.CreateCounter("mxgateway.retries.attempted"); - _workerStartupLatencyHistogram = _meter.CreateHistogram("mxgateway.workers.startup.duration", "ms"); - _commandLatencyHistogram = _meter.CreateHistogram("mxgateway.commands.duration", "ms"); - _eventStreamSendLatencyHistogram = _meter.CreateHistogram("mxgateway.events.stream_send.duration", "ms"); + _workerStartupLatencyHistogram = _meter.CreateHistogram("mxgateway.workers.startup.duration", "s"); + _commandLatencyHistogram = _meter.CreateHistogram("mxgateway.commands.duration", "s"); + _eventStreamSendLatencyHistogram = _meter.CreateHistogram("mxgateway.events.stream_send.duration", "s"); _meter.CreateObservableGauge("mxgateway.sessions.open", GetOpenSessions); _meter.CreateObservableGauge("mxgateway.workers.running", GetWorkersRunning); @@ -144,7 +144,7 @@ public sealed class GatewayMetrics : IDisposable _workersRunning++; } - _workerStartupLatencyHistogram.Record(startupDuration.TotalMilliseconds); + _workerStartupLatencyHistogram.Record(startupDuration.TotalSeconds); } /// @@ -208,7 +208,7 @@ public sealed class GatewayMetrics : IDisposable KeyValuePair methodTag = new("method", method); _commandsSucceededCounter.Add(1, methodTag); - _commandLatencyHistogram.Record(duration.TotalMilliseconds, methodTag); + _commandLatencyHistogram.Record(duration.TotalSeconds, methodTag); } /// @@ -228,7 +228,7 @@ public sealed class GatewayMetrics : IDisposable KeyValuePair methodTag = new("method", method); KeyValuePair categoryTag = new("category", category); _commandsFailedCounter.Add(1, methodTag, categoryTag); - _commandLatencyHistogram.Record(duration.TotalMilliseconds, methodTag, categoryTag); + _commandLatencyHistogram.Record(duration.TotalSeconds, methodTag, categoryTag); } /// @@ -255,7 +255,7 @@ public sealed class GatewayMetrics : IDisposable public void RecordEventStreamSend(string family, TimeSpan duration) { _eventStreamSendLatencyHistogram.Record( - duration.TotalMilliseconds, + duration.TotalSeconds, new KeyValuePair("family", family)); } From 3965a7741eebaa56b8cb0e693a414f7f35198fab Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 16:44:40 -0400 Subject: [PATCH 3/4] feat(mxgateway): config-driven OTLP exporter opt-in (default Prometheus) --- src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs index b1931a9..3483d37 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs @@ -78,6 +78,11 @@ public static class GatewayApplication { o.ServiceName = "mxgateway"; o.Meters = [GatewayMetrics.MeterName]; // "MxGateway.Server" — name unchanged + if (Enum.TryParse(builder.Configuration["MxGateway:Telemetry:Exporter"], ignoreCase: true, out var exporter)) + o.Exporter = exporter; + var otlp = builder.Configuration["MxGateway:Telemetry:OtlpEndpoint"]; + if (!string.IsNullOrWhiteSpace(otlp)) + o.OtlpEndpoint = otlp; }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From dbf550da8b38cb8f0bfdca5c2d002d960f4442d3 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 16:48:46 -0400 Subject: [PATCH 4/4] docs(mxgateway): sync Metrics.md to renamed meter + seconds histogram units --- docs/Metrics.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Metrics.md b/docs/Metrics.md index 2ab0e6c..202b00a 100644 --- a/docs/Metrics.md +++ b/docs/Metrics.md @@ -4,7 +4,7 @@ The metrics subsystem exposes counters, histograms, and observable gauges that d ## Overview -`GatewayMetrics` is a singleton (registered in `GatewayApplication.cs`) that owns a single `Meter` named `ZB.MOM.WW.MxGateway.Server` and a set of synchronised counters, histograms, and observable gauges. Subsystems call typed mutator methods (`SessionOpened`, `CommandFailed`, `EventReceived`, etc.) rather than touching the `Meter` directly, which keeps the OpenTelemetry instrument names and tag conventions in one place. A `lock (_syncRoot)` block guards the scalar fields used by `GetSnapshot`, while per-event maps use `ConcurrentDictionary` so the hot event path avoids the lock. +`GatewayMetrics` is a singleton (registered in `GatewayApplication.cs`) that owns a single `Meter` named `ZB.MOM.WW.MxGateway` and a set of synchronised counters, histograms, and observable gauges. Subsystems call typed mutator methods (`SessionOpened`, `CommandFailed`, `EventReceived`, etc.) rather than touching the `Meter` directly, which keeps the OpenTelemetry instrument names and tag conventions in one place. A `lock (_syncRoot)` block guards the scalar fields used by `GetSnapshot`, while per-event maps use `ConcurrentDictionary` so the hot event path avoids the lock. ## Meter and OpenTelemetry Compatibility @@ -13,7 +13,7 @@ The meter name is exposed as a constant so that hosting code can register it wit ```csharp public sealed class GatewayMetrics : IDisposable { - public const string MeterName = "ZB.MOM.WW.MxGateway.Server"; + public const string MeterName = "ZB.MOM.WW.MxGateway"; public GatewayMetrics() { @@ -50,12 +50,12 @@ All counters are `Counter`. Tag values come from the call sites listed und ### Histograms -Histograms record durations in milliseconds (the `unit` argument on `CreateHistogram`): +Histograms record durations in seconds (the `unit` argument on `CreateHistogram`): ```csharp -_workerStartupLatencyHistogram = _meter.CreateHistogram("mxgateway.workers.startup.duration", "ms"); -_commandLatencyHistogram = _meter.CreateHistogram("mxgateway.commands.duration", "ms"); -_eventStreamSendLatencyHistogram = _meter.CreateHistogram("mxgateway.events.stream_send.duration", "ms"); +_workerStartupLatencyHistogram = _meter.CreateHistogram("mxgateway.workers.startup.duration", "s"); +_commandLatencyHistogram = _meter.CreateHistogram("mxgateway.commands.duration", "s"); +_eventStreamSendLatencyHistogram = _meter.CreateHistogram("mxgateway.events.stream_send.duration", "s"); ``` | Instrument | Tags | What it measures |