using Microsoft.Extensions.Configuration; using ZB.MOM.WW.LocalDb.Replication; using ZB.MOM.WW.OtOpcUa.Commons.Observability; using ZB.MOM.WW.Telemetry; namespace ZB.MOM.WW.OtOpcUa.Host.Observability; /// /// Wires the OtOpcUa Meter + ActivitySource into OpenTelemetry and exposes a Prometheus /// scrape endpoint at /metrics on the host pipeline. F13d slice — only the meter + /// activity source declared in are surfaced; per-Akka /// internals + ASP.NET request metrics stay off by default to keep the scrape payload /// scoped to OtOpcUa-owned signals. /// public static class ObservabilityExtensions { /// Adds OtOpcUa observability (metrics and tracing) to the service collection. /// The service collection to add observability services to. /// /// Configuration read for the opt-in OTLP exporter. OtOpcUa:Telemetry:Exporter /// (parsed case-insensitively to ) switches to OTLP when set to /// Otlp; OtOpcUa:Telemetry:OtlpEndpoint sets the OTLP endpoint. With no /// config the exporter stays Prometheus (the default). /// /// The service collection, for chaining. public static IServiceCollection AddOtOpcUaObservability(this IServiceCollection services, IConfiguration configuration) { return services.AddZbTelemetry(o => { o.ServiceName = "otopcua"; // o.Meters is a STRICT allowlist — ZbTelemetry adds only the named meters, with no // wildcard. The LocalDb replication engine publishes under its own meter name, so // without this entry its localdb.sync.* / localdb.oplog.depth series are silently absent // from /metrics on driver nodes (the exact omission a ScadaBridge live gate caught). o.Meters = [OtOpcUaTelemetry.MeterName, LocalDbMetrics.MeterName]; o.ActivitySources = [OtOpcUaTelemetry.ActivitySourceName]; if (Enum.TryParse(configuration["OtOpcUa:Telemetry:Exporter"], ignoreCase: true, out var exporter)) o.Exporter = exporter; var otlp = configuration["OtOpcUa:Telemetry:OtlpEndpoint"]; if (!string.IsNullOrWhiteSpace(otlp)) o.OtlpEndpoint = otlp; }); } /// /// Mounts the Prometheus /metrics scrape endpoint on the existing ASP.NET pipeline. /// The endpoint is explicitly marked AllowAnonymous so unauthenticated Prometheus /// scrapers can reach it regardless of the host's auth fallback policy (which on admin-role /// nodes is RequireAuthenticatedUser). This mirrors the behaviour of /// MapZbHealth, which also marks its endpoints anonymous. /// /// The endpoint route builder. /// The endpoint route builder, for chaining. public static IEndpointRouteBuilder MapOtOpcUaMetrics(this IEndpointRouteBuilder app) { app.MapZbMetrics().AllowAnonymous(); return app; } }