From 9ad17e2964483d29ecd13e1f24a60bc06bfbecd2 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 15:29:43 -0400 Subject: [PATCH 1/5] build(mxgateway): reference ZB.MOM.WW.Telemetry + Serilog packages --- nuget.config | 2 ++ .../ZB.MOM.WW.MxGateway.Server.csproj | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/nuget.config b/nuget.config index 5223539..a2b8c8a 100644 --- a/nuget.config +++ b/nuget.config @@ -14,6 +14,8 @@ + + diff --git a/src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj b/src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj index c8c8527..855acc4 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj +++ b/src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj @@ -7,6 +7,11 @@ + + + + + From e39972357bf8b18091127a019ec7d2cd2f9a8a3f Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 15:32:38 -0400 Subject: [PATCH 2/5] config(mxgateway): translate MEL Logging section to Serilog --- .../appsettings.Development.json | 13 +++++++++---- src/ZB.MOM.WW.MxGateway.Server/appsettings.json | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ZB.MOM.WW.MxGateway.Server/appsettings.Development.json b/src/ZB.MOM.WW.MxGateway.Server/appsettings.Development.json index 0c208ae..674b2e0 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/appsettings.Development.json +++ b/src/ZB.MOM.WW.MxGateway.Server/appsettings.Development.json @@ -1,8 +1,13 @@ { - "Logging": { - "LogLevel": { + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } + "Override": { "Microsoft.AspNetCore": "Warning" } + }, + "WriteTo": [ + { "Name": "Console" }, + { "Name": "File", "Args": { "path": "logs/mxgateway-.log", "rollingInterval": "Day" } } + ] } } diff --git a/src/ZB.MOM.WW.MxGateway.Server/appsettings.json b/src/ZB.MOM.WW.MxGateway.Server/appsettings.json index 63928c7..bc565ee 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/appsettings.json +++ b/src/ZB.MOM.WW.MxGateway.Server/appsettings.json @@ -1,9 +1,14 @@ { - "Logging": { - "LogLevel": { + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } + "Override": { "Microsoft.AspNetCore": "Warning" } + }, + "WriteTo": [ + { "Name": "Console" }, + { "Name": "File", "Args": { "path": "logs/mxgateway-.log", "rollingInterval": "Day" } } + ] }, "AllowedHosts": "*", "MxGateway": { From c51b6f9ce40307adcaac5b760b44ab4e22eeddce Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 15:43:10 -0400 Subject: [PATCH 3/5] =?UTF-8?q?feat(mxgateway):=20adopt=20AddZbSerilog=20?= =?UTF-8?q?=E2=80=94=20MEL=E2=86=92Serilog=20provider=20swap=20(behaviour-?= =?UTF-8?q?preserving)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs | 3 +++ .../Gateway/GatewayApplicationTests.cs | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs index beba939..4ff780f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs @@ -15,6 +15,7 @@ using ZB.MOM.WW.MxGateway.Server.Security.Authentication; using ZB.MOM.WW.MxGateway.Server.Security.Authorization; using ZB.MOM.WW.MxGateway.Server.Sessions; using ZB.MOM.WW.MxGateway.Server.Workers; +using ZB.MOM.WW.Telemetry.Serilog; namespace ZB.MOM.WW.MxGateway.Server; @@ -61,6 +62,8 @@ public static class GatewayApplication ConfigureSelfSignedTls(builder); + builder.AddZbSerilog(o => o.ServiceName = "mxgateway"); + builder.Services.AddGatewayConfiguration(); builder.Services.AddSqliteAuthStore(); builder.Services.AddGatewayGrpcAuthorization(); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs index 9fb3f05..1205d72 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using ZB.MOM.WW.MxGateway.Server; using ZB.MOM.WW.MxGateway.Server.Dashboard; @@ -29,6 +30,15 @@ public sealed class GatewayApplicationTests Assert.DoesNotContain("/health/live", paths); } + /// Verifies that Build registers Serilog as the host logging provider. + [Fact] + public void Build_UsesSerilogLoggerProvider() + { + using var app = GatewayApplication.Build([]); + var factory = app.Services.GetRequiredService(); + Assert.Equal("SerilogLoggerFactory", factory.GetType().Name); + } + /// Verifies that Build registers the gateway metrics service. [Fact] public async Task Build_RegistersGatewayMetrics() From f1129b969daadb792d23af65721b4a9176dad1f8 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 15:49:32 -0400 Subject: [PATCH 4/5] feat(mxgateway): expose GatewayLogRedactor via shared ILogRedactor seam --- .../Diagnostics/GatewayLogRedactorSeam.cs | 28 +++++++++++++++++++ .../GatewayApplication.cs | 1 + .../GatewayLogRedactorSeamTests.cs | 15 ++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactorSeam.cs create mode 100644 src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs diff --git a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactorSeam.cs b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactorSeam.cs new file mode 100644 index 0000000..0beab89 --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactorSeam.cs @@ -0,0 +1,28 @@ +using ZB.MOM.WW.Telemetry.Serilog; + +namespace ZB.MOM.WW.MxGateway.Server.Diagnostics; + +/// +/// Adapts the static to the shared seam +/// so the telemetry RedactionEnricher masks API-key/credential material on every log event. +/// +public sealed class GatewayLogRedactorSeam : ILogRedactor +{ + private static readonly string[] IdentityKeys = ["ClientIdentity", "authorization", "Authorization"]; + + /// + /// Masks API-key/credential material in known identity-bearing log properties. + /// + /// The log event property dictionary to redact in place. + public void Redact(IDictionary properties) + { + ArgumentNullException.ThrowIfNull(properties); + foreach (var key in IdentityKeys) + { + if (properties.TryGetValue(key, out var value) && value is string s) + { + properties[key] = GatewayLogRedactor.RedactClientIdentity(s); + } + } + } +} diff --git a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs index 4ff780f..621e630 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs @@ -73,6 +73,7 @@ public static class GatewayApplication failureStatus: null, tags: new[] { ZbHealthTags.Ready }); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs new file mode 100644 index 0000000..494808e --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using ZB.MOM.WW.MxGateway.Server.Diagnostics; +using Xunit; + +public class GatewayLogRedactorSeamTests +{ + [Fact] + public void Redact_MasksApiKeyInClientIdentity() + { + var redactor = new GatewayLogRedactorSeam(); + var props = new Dictionary { ["ClientIdentity"] = "Bearer mxgw_operator01_super-secret" }; + redactor.Redact(props); + Assert.Equal("Bearer mxgw_operator01_[redacted]", props["ClientIdentity"]); + } +} From 9912389fa18dfce4243dfc59f597cb33a2ab34ef Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 15:53:46 -0400 Subject: [PATCH 5/5] feat(mxgateway): export GatewayMetrics via AddZbTelemetry + /metrics (name/units unchanged) --- .../GatewayApplication.cs | 7 ++++++ .../Gateway/GatewayApplicationTests.cs | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs index 621e630..b1931a9 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs @@ -15,6 +15,7 @@ using ZB.MOM.WW.MxGateway.Server.Security.Authentication; using ZB.MOM.WW.MxGateway.Server.Security.Authorization; using ZB.MOM.WW.MxGateway.Server.Sessions; using ZB.MOM.WW.MxGateway.Server.Workers; +using ZB.MOM.WW.Telemetry; using ZB.MOM.WW.Telemetry.Serilog; namespace ZB.MOM.WW.MxGateway.Server; @@ -73,6 +74,11 @@ public static class GatewayApplication failureStatus: null, tags: new[] { ZbHealthTags.Ready }); builder.Services.AddSingleton(); + builder.AddZbTelemetry(o => + { + o.ServiceName = "mxgateway"; + o.Meters = [GatewayMetrics.MeterName]; // "MxGateway.Server" — name unchanged + }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -179,6 +185,7 @@ public static class GatewayApplication endpoints.MapStaticAssets(ResolveStaticAssetsManifestPath()); endpoints.MapZbHealth(); + endpoints.MapZbMetrics(); endpoints.MapGrpcService(); endpoints.MapGrpcService(); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs index 1205d72..e5d5e91 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs @@ -1,3 +1,5 @@ +using System.Net; +using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Routing; @@ -50,6 +52,28 @@ public sealed class GatewayApplicationTests Assert.NotNull(metrics); } + /// Verifies that Build mounts the Prometheus /metrics scrape endpoint. + [Fact] + public async Task Build_MapsMetricsEndpoint() + { + // Bind an ephemeral port (:0) — xUnit runs test collections in parallel, so any + // started-host test must avoid a fixed port to prevent a bind collision. + await using WebApplication app = GatewayApplication.Build(["--urls=http://127.0.0.1:0"]); + await app.StartAsync(); + try + { + using var client = new HttpClient { BaseAddress = new Uri(app.Urls.First()) }; + + using HttpResponseMessage response = await client.GetAsync("/metrics"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + finally + { + await app.StopAsync(); + } + } + /// Verifies that Build maps dashboard and authentication endpoints when the dashboard is enabled. [Fact] public async Task Build_WhenDashboardEnabled_MapsBlazorDashboardAndAuthEndpoints()