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;
}
}