diff --git a/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs new file mode 100644 index 0000000..15b0aae --- /dev/null +++ b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using OpenTelemetry.Resources; + +namespace ZB.MOM.WW.Telemetry; + +/// +/// Builds the shared OpenTelemetry ResourceBuilder from . +/// Used internally by AddZbTelemetry so metrics, traces, and logs carry an identical +/// Resource. Exposed for tests and custom pipelines. +/// +public static class ZbResource +{ + /// + /// Returns a pre-populated with service.name, + /// service.namespace, service.version, site.id, node.role, and + /// host.name (always ). Attributes with + /// null values are omitted from the Resource. + /// + /// The telemetry options describing the service identity. + public static ResourceBuilder Build(ZbTelemetryOptions options) => + Configure(ResourceBuilder.CreateDefault(), options); + + /// + /// Applies the shared ZB.MOM.WW Resource attributes to an existing . + /// Internal seam so the AddZbTelemetry pipeline produces a Resource identical to + /// . + /// + internal static ResourceBuilder Configure(ResourceBuilder builder, ZbTelemetryOptions options) + { + builder.AddService( + serviceName: options.ServiceName, + serviceNamespace: options.ServiceNamespace, + serviceVersion: options.ServiceVersion); + + var attributes = new List> + { + new("host.name", System.Environment.MachineName), + }; + + if (!string.IsNullOrEmpty(options.SiteId)) + { + attributes.Add(new("site.id", options.SiteId)); + } + + if (!string.IsNullOrEmpty(options.NodeRole)) + { + attributes.Add(new("node.role", options.NodeRole)); + } + + builder.AddAttributes(attributes); + return builder; + } +} diff --git a/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs new file mode 100644 index 0000000..f52b1b3 --- /dev/null +++ b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs @@ -0,0 +1,76 @@ +namespace ZB.MOM.WW.Telemetry; + +/// +/// Selects how instrumentation data is exported. +/// +public enum ZbExporter +{ + /// + /// Prometheus scrape endpoint (default). Call app.MapZbMetrics() to mount /metrics. + /// + Prometheus, + + /// + /// OTLP gRPC export. Set + /// (e.g. "http://collector:4317"). + /// + Otlp, +} + +/// +/// Options for AddZbTelemetry. All properties feed the shared OpenTelemetry Resource. +/// +public sealed class ZbTelemetryOptions +{ + /// + /// Required. Short lower-case app identifier — e.g. "otopcua", "mxgateway", + /// "scadabridge". Populates OTel Resource service.name. + /// + public string ServiceName { get; set; } = ""; + + /// + /// Fleet-wide namespace. Default "ZB.MOM.WW". Do not override per-app. + /// Populates OTel Resource service.namespace. + /// + public string ServiceNamespace { get; set; } = "ZB.MOM.WW"; + + /// + /// Optional. Populate from AssemblyInformationalVersion. + /// Populates OTel Resource service.version. + /// + public string? ServiceVersion { get; set; } + + /// + /// Optional. Physical or logical site identifier. + /// Populates OTel Resource site.id. + /// + public string? SiteId { get; set; } + + /// + /// Optional. Node function: "central", "site", "hub", "standalone". + /// Populates OTel Resource node.role. + /// + public string? NodeRole { get; set; } + + /// + /// App-specific Meter names to register with the OTel MeterProvider. Standard instrumentation + /// meters are added automatically (ASP.NET Core, HttpClient, runtime, process). + /// + public string[] Meters { get; set; } = []; + + /// + /// App-specific ActivitySource names to register with the OTel TracerProvider. + /// + public string[] ActivitySources { get; set; } = []; + + /// + /// Export path. Default Prometheus; use for a real collector. + /// + public ZbExporter Exporter { get; set; } = ZbExporter.Prometheus; + + /// + /// Required when = . + /// OTLP gRPC endpoint, e.g. "http://collector:4317". + /// + public string? OtlpEndpoint { get; set; } +} diff --git a/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs b/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs new file mode 100644 index 0000000..e9f5de8 --- /dev/null +++ b/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs @@ -0,0 +1,50 @@ +using OpenTelemetry.Resources; +using ZB.MOM.WW.Telemetry; + +namespace ZB.MOM.WW.Telemetry.Tests; + +public sealed class ZbResourceTests +{ + [Fact] + public void Build_PopulatesAllResourceAttributes() + { + var options = new ZbTelemetryOptions + { + ServiceName = "otopcua", + ServiceNamespace = "ZB.MOM.WW", + ServiceVersion = "1.2.3", + SiteId = "site-7", + NodeRole = "central", + }; + + var resource = ZbResource.Build(options).Build(); + var attributes = resource.Attributes.ToDictionary(a => a.Key, a => a.Value); + + Assert.Equal("otopcua", attributes["service.name"]); + Assert.Equal("ZB.MOM.WW", attributes["service.namespace"]); + Assert.Equal("1.2.3", attributes["service.version"]); + Assert.Equal("site-7", attributes["site.id"]); + Assert.Equal("central", attributes["node.role"]); + Assert.Equal(Environment.MachineName, attributes["host.name"]); + } + + [Fact] + public void Build_OmitsOptionalAttributes_WhenNull() + { + var options = new ZbTelemetryOptions + { + ServiceName = "mxgateway", + // ServiceVersion / SiteId / NodeRole left null + }; + + var resource = ZbResource.Build(options).Build(); + var keys = resource.Attributes.Select(a => a.Key).ToHashSet(); + + Assert.Contains("service.name", keys); + Assert.Contains("service.namespace", keys); + Assert.Contains("host.name", keys); + Assert.DoesNotContain("service.version", keys); + Assert.DoesNotContain("site.id", keys); + Assert.DoesNotContain("node.role", keys); + } +}