From ef53553e9db0ce1afbc46ba9775e6f02185966b7 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 20 Apr 2026 00:41:16 -0400 Subject: [PATCH] =?UTF-8?q?OTel=20Prometheus=20exporter=20wiring=20?= =?UTF-8?q?=E2=80=94=20RedundancyMetrics=20meter=20now=20scraped=20at=20/m?= =?UTF-8?q?etrics.=20Closes=20task=20#201.=20Picked=20Prometheus=20over=20?= =?UTF-8?q?OTLP=20per=20the=20earlier=20recommendation=20(pull-based=20mea?= =?UTF-8?q?ns=20no=20OTel=20Collector=20deployment=20required=20for=20the?= =?UTF-8?q?=20common=20K8s/containers=20case;=20the=20endpoint=20ASP.NET-h?= =?UTF-8?q?osts=20inside=20the=20Admin=20app=20already,=20so=20one=20less?= =?UTF-8?q?=20moving=20part).=20Adds=20two=20NuGet=20refs=20to=20the=20Adm?= =?UTF-8?q?in=20csproj:=20OpenTelemetry.Extensions.Hosting=201.15.2=20(sta?= =?UTF-8?q?ble)=20+=20OpenTelemetry.Exporter.Prometheus.AspNetCore=201.15.?= =?UTF-8?q?2-beta.1=20(the=20exporter=20has=20historically=20been=20beta-o?= =?UTF-8?q?nly;=20rest=20of=20the=20OTel=20ecosystem=20treats=20it=20as=20?= =?UTF-8?q?production-acceptable=20+=20it's=20what=20the=20upstream=20OTel?= =?UTF-8?q?=20docs=20themselves=20recommend=20for=20AspNetCore=20hosts).?= =?UTF-8?q?=20Program.cs=20gains=20a=20Metrics:Prometheus:Enabled=20toggle?= =?UTF-8?q?=20(defaults=20true;=20setting=20to=20false=20disables=20both?= =?UTF-8?q?=20the=20MeterProvider=20registration=20+=20the=20scrape=20endp?= =?UTF-8?q?oint=20entirely=20for=20locked-down=20deployments).=20When=20en?= =?UTF-8?q?abled,=20AddOpenTelemetry().WithMetrics()=20registers=20a=20Met?= =?UTF-8?q?erProvider=20that=20subscribes=20to=20the=20"ZB.MOM.WW.OtOpcUa.?= =?UTF-8?q?Redundancy"=20meter=20(the=20exact=20MeterName=20constant=20on?= =?UTF-8?q?=20RedundancyMetrics)=20+=20wires=20AddPrometheusExporter.=20Ma?= =?UTF-8?q?pPrometheusScrapingEndpoint()=20appends=20a=20/metrics=20handle?= =?UTF-8?q?r=20producing=20the=20Prometheus=20text-format=20output;=20deli?= =?UTF-8?q?berately=20NOT=20authenticated=20because=20scrape=20jobs=20typi?= =?UTF-8?q?cally=20run=20on=20a=20trusted=20network=20+=20operators=20who?= =?UTF-8?q?=20need=20auth=20wrap=20the=20endpoint=20behind=20a=20reverse-p?= =?UTF-8?q?roxy=20basic-auth=20gate=20per=20fleet-ops=20convention.=20apps?= =?UTF-8?q?ettings.json=20declares=20the=20toggle=20with=20Enabled:=20true?= =?UTF-8?q?=20so=20the=20default=20deploy=20gets=20metrics=20automatically?= =?UTF-8?q?=20=E2=80=94=20turning=20off=20is=20the=20explicit=20action.=20?= =?UTF-8?q?Future=20meters=20(resilience=20tracker=20+=20host=20status=20+?= =?UTF-8?q?=20auth=20probe)=20just=20AddMeter("Name")=20alongside=20the=20?= =?UTF-8?q?existing=20call=20to=20start=20flowing=20through=20the=20same?= =?UTF-8?q?=20endpoint=20without=20more=20infrastructure.=20Admin=20projec?= =?UTF-8?q?t=20builds=200=20errors;=20Admin.Tests=2092/92=20passing=20(unc?= =?UTF-8?q?hanged=20=E2=80=94=20the=20OTel=20pipeline=20runs=20at=20reques?= =?UTF-8?q?t=20time,=20not=20test=20time).=20Still-pending=20work=20that?= =?UTF-8?q?=20was=20NOT=20part=20of=20#201's=20scope:=20an=20equivalent=20?= =?UTF-8?q?setup=20for=20the=20Server=20project=20(different=20MeterNames?= =?UTF-8?q?=20=E2=80=94=20the=20Polly=20pipeline=20builder's=20tracker=20+?= =?UTF-8?q?=20host-status=20publisher)=20+=20a=20metrics=20cheat-sheet=20i?= =?UTF-8?q?n=20docs/observability.md=20documenting=20each=20meter's=20tag?= =?UTF-8?q?=20set=20+=20expected=20alerting=20thresholds.=20Those=20are=20?= =?UTF-8?q?natural=20follow-ups=20when=20fleet-ops=20starts=20building=20d?= =?UTF-8?q?ashboards.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ZB.MOM.WW.OtOpcUa.Admin/Program.cs | 23 +++++++++++++++++++ .../ZB.MOM.WW.OtOpcUa.Admin.csproj | 2 ++ src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json | 5 ++++ 3 files changed, 30 insertions(+) diff --git a/src/ZB.MOM.WW.OtOpcUa.Admin/Program.cs b/src/ZB.MOM.WW.OtOpcUa.Admin/Program.cs index 0089e24..135f6b9 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Admin/Program.cs +++ b/src/ZB.MOM.WW.OtOpcUa.Admin/Program.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.EntityFrameworkCore; +using OpenTelemetry.Metrics; using Serilog; using ZB.MOM.WW.OtOpcUa.Admin.Components; using ZB.MOM.WW.OtOpcUa.Admin.Hubs; @@ -70,6 +71,19 @@ builder.Services.AddScoped(); // SignalR real-time fleet status + alerts (admin-ui.md §"Real-Time Updates"). builder.Services.AddHostedService(); +// OpenTelemetry Prometheus exporter — Meter stream from RedundancyMetrics + any future +// Admin-side instrumentation lands on the /metrics endpoint Prometheus scrapes. Pull-based +// means no OTel Collector deployment required for the common deploy-in-a-K8s case; appsettings +// Metrics:Prometheus:Enabled=false disables the endpoint entirely for locked-down deployments. +var metricsEnabled = builder.Configuration.GetValue("Metrics:Prometheus:Enabled", true); +if (metricsEnabled) +{ + builder.Services.AddOpenTelemetry() + .WithMetrics(m => m + .AddMeter(RedundancyMetrics.MeterName) + .AddPrometheusExporter()); +} + var app = builder.Build(); app.UseSerilogRequestLogging(); @@ -87,6 +101,15 @@ app.MapPost("/auth/logout", async (HttpContext ctx) => app.MapHub("/hubs/fleet"); app.MapHub("/hubs/alerts"); +if (metricsEnabled) +{ + // Prometheus scrape endpoint — expose instrumentation registered in the OTel MeterProvider + // above. Emits text-format metrics at /metrics; auth is intentionally NOT required (Prometheus + // scrape jobs typically run on a trusted network). Operators who need auth put the endpoint + // behind a reverse-proxy basic-auth gate per fleet-ops convention. + app.MapPrometheusScrapingEndpoint(); +} + app.MapRazorComponents().AddInteractiveServerRenderMode(); await app.RunAsync(); diff --git a/src/ZB.MOM.WW.OtOpcUa.Admin/ZB.MOM.WW.OtOpcUa.Admin.csproj b/src/ZB.MOM.WW.OtOpcUa.Admin/ZB.MOM.WW.OtOpcUa.Admin.csproj index 1fa7aa4..3a322a8 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Admin/ZB.MOM.WW.OtOpcUa.Admin.csproj +++ b/src/ZB.MOM.WW.OtOpcUa.Admin/ZB.MOM.WW.OtOpcUa.Admin.csproj @@ -16,6 +16,8 @@ + + diff --git a/src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json b/src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json index 24d73fd..cbb842a 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json +++ b/src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json @@ -23,5 +23,10 @@ }, "Serilog": { "MinimumLevel": "Information" + }, + "Metrics": { + "Prometheus": { + "Enabled": true + } } } -- 2.49.1