0b90379f70
ZbTelemetryOptions.Meters/ActivitySources are strict allowlists. The host named only ZB.MOM.WW.OtOpcUa and the LocalDb meter, so the in-process Galaxy driver's galaxy.* spans and its event pump's galaxy.events.* counters (both on "ZB.MOM.WW.OtOpcUa.Driver.Galaxy") were silently dropped from /metrics and OTLP. Found exercising OTLP end to end against a collector (scadaproj#13). - Galaxy driver: public GalaxyDriverTelemetryNames (MeterName / ActivitySourceName); GalaxyTelemetry and EventPump reference it. - Host: ObservabilityExtensions.ObservedMeters / ObservedActivitySources include it. - Host.IntegrationTests: ObservabilityAllowlistTests assert on the listener side (span sampled, instrument enabled) with an unregistered-name negative control; dropping the Galaxy entries fails 3 of 8. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
88 lines
3.6 KiB
C#
88 lines
3.6 KiB
C#
using System.Diagnostics;
|
|
using System.Diagnostics.Metrics;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using OpenTelemetry.Metrics;
|
|
using OpenTelemetry.Trace;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.LocalDb.Replication;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Observability;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy;
|
|
using ZB.MOM.WW.OtOpcUa.Host.Observability;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// scadaproj#13 — <c>ZbTelemetryOptions.Meters</c> / <c>.ActivitySources</c> are strict allowlists,
|
|
/// so a meter or activity source the host forgets to name is silently never exported (the Galaxy
|
|
/// driver's <c>galaxy.*</c> spans and <c>galaxy.events.*</c> counters were). These tests build the
|
|
/// real <see cref="ObservabilityExtensions.AddOtOpcUaObservability"/> container, start its
|
|
/// providers, and assert on the listener side: a span from a named source is sampled, and an
|
|
/// instrument on a named meter is enabled. An unregistered name is the negative control proving
|
|
/// the assertion can fail.
|
|
/// </summary>
|
|
public sealed class ObservabilityAllowlistTests : IAsyncDisposable
|
|
{
|
|
private readonly WebApplication _app;
|
|
|
|
public ObservabilityAllowlistTests()
|
|
{
|
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions { Args = [] });
|
|
builder.Services.AddOtOpcUaObservability(builder.Configuration);
|
|
_app = builder.Build();
|
|
// Resolving the providers is what attaches their ActivityListener / MeterListener.
|
|
_app.Services.GetRequiredService<TracerProvider>();
|
|
_app.Services.GetRequiredService<MeterProvider>();
|
|
}
|
|
|
|
[Fact]
|
|
public void ObservedLists_NameTheGalaxyDriverMeterAndSource()
|
|
{
|
|
ObservabilityExtensions.ObservedMeters.ShouldBe(
|
|
[OtOpcUaTelemetry.MeterName, LocalDbMetrics.MeterName, GalaxyDriverTelemetryNames.MeterName],
|
|
ignoreOrder: true);
|
|
ObservabilityExtensions.ObservedActivitySources.ShouldBe(
|
|
[OtOpcUaTelemetry.ActivitySourceName, GalaxyDriverTelemetryNames.ActivitySourceName],
|
|
ignoreOrder: true);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OtOpcUaTelemetry.ActivitySourceName)]
|
|
[InlineData(GalaxyDriverTelemetryNames.ActivitySourceName)]
|
|
public void SpanFromAnObservedSource_IsRecorded(string sourceName)
|
|
{
|
|
using var source = new ActivitySource(sourceName);
|
|
using var activity = source.StartActivity("allowlist-probe");
|
|
activity.ShouldNotBeNull();
|
|
activity.IsAllDataRequested.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void SpanFromAnUnregisteredSource_IsDropped_NegativeControl()
|
|
{
|
|
using var source = new ActivitySource("ZB.MOM.WW.OtOpcUa.Unregistered." + Guid.NewGuid().ToString("N"));
|
|
source.StartActivity("allowlist-probe").ShouldBeNull();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OtOpcUaTelemetry.MeterName)]
|
|
[InlineData(LocalDbMetrics.MeterName)]
|
|
[InlineData(GalaxyDriverTelemetryNames.MeterName)]
|
|
public void InstrumentOnAnObservedMeter_IsEnabled(string meterName)
|
|
{
|
|
using var meter = new Meter(meterName);
|
|
var counter = meter.CreateCounter<long>("allowlist.probe." + Guid.NewGuid().ToString("N"));
|
|
counter.Enabled.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void InstrumentOnAnUnregisteredMeter_IsNotEnabled_NegativeControl()
|
|
{
|
|
using var meter = new Meter("ZB.MOM.WW.OtOpcUa.Unregistered." + Guid.NewGuid().ToString("N"));
|
|
meter.CreateCounter<long>("allowlist.probe").Enabled.ShouldBeFalse();
|
|
}
|
|
|
|
public async ValueTask DisposeAsync() => await _app.DisposeAsync();
|
|
}
|