feat(telemetry): options + shared OTel Resource
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenTelemetry.Resources;
|
||||
|
||||
namespace ZB.MOM.WW.Telemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the shared OpenTelemetry ResourceBuilder from <see cref="ZbTelemetryOptions"/>.
|
||||
/// Used internally by <c>AddZbTelemetry</c> so metrics, traces, and logs carry an identical
|
||||
/// Resource. Exposed for tests and custom pipelines.
|
||||
/// </summary>
|
||||
public static class ZbResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a <see cref="ResourceBuilder"/> pre-populated with <c>service.name</c>,
|
||||
/// <c>service.namespace</c>, <c>service.version</c>, <c>site.id</c>, <c>node.role</c>, and
|
||||
/// <c>host.name</c> (always <see cref="System.Environment.MachineName"/>). Attributes with
|
||||
/// null values are omitted from the Resource.
|
||||
/// </summary>
|
||||
/// <param name="options">The telemetry options describing the service identity.</param>
|
||||
public static ResourceBuilder Build(ZbTelemetryOptions options) =>
|
||||
Configure(ResourceBuilder.CreateDefault(), options);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the shared ZB.MOM.WW Resource attributes to an existing <see cref="ResourceBuilder"/>.
|
||||
/// Internal seam so the <c>AddZbTelemetry</c> pipeline produces a Resource identical to
|
||||
/// <see cref="Build"/>.
|
||||
/// </summary>
|
||||
internal static ResourceBuilder Configure(ResourceBuilder builder, ZbTelemetryOptions options)
|
||||
{
|
||||
builder.AddService(
|
||||
serviceName: options.ServiceName,
|
||||
serviceNamespace: options.ServiceNamespace,
|
||||
serviceVersion: options.ServiceVersion);
|
||||
|
||||
var attributes = new List<KeyValuePair<string, object>>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
namespace ZB.MOM.WW.Telemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Selects how instrumentation data is exported.
|
||||
/// </summary>
|
||||
public enum ZbExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Prometheus scrape endpoint (default). Call <c>app.MapZbMetrics()</c> to mount <c>/metrics</c>.
|
||||
/// </summary>
|
||||
Prometheus,
|
||||
|
||||
/// <summary>
|
||||
/// OTLP gRPC export. Set <see cref="ZbTelemetryOptions.OtlpEndpoint"/>
|
||||
/// (e.g. <c>"http://collector:4317"</c>).
|
||||
/// </summary>
|
||||
Otlp,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for <c>AddZbTelemetry</c>. All properties feed the shared OpenTelemetry Resource.
|
||||
/// </summary>
|
||||
public sealed class ZbTelemetryOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Required. Short lower-case app identifier — e.g. <c>"otopcua"</c>, <c>"mxgateway"</c>,
|
||||
/// <c>"scadabridge"</c>. Populates OTel Resource <c>service.name</c>.
|
||||
/// </summary>
|
||||
public string ServiceName { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Fleet-wide namespace. Default <c>"ZB.MOM.WW"</c>. Do not override per-app.
|
||||
/// Populates OTel Resource <c>service.namespace</c>.
|
||||
/// </summary>
|
||||
public string ServiceNamespace { get; set; } = "ZB.MOM.WW";
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Populate from <c>AssemblyInformationalVersion</c>.
|
||||
/// Populates OTel Resource <c>service.version</c>.
|
||||
/// </summary>
|
||||
public string? ServiceVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Physical or logical site identifier.
|
||||
/// Populates OTel Resource <c>site.id</c>.
|
||||
/// </summary>
|
||||
public string? SiteId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Node function: <c>"central"</c>, <c>"site"</c>, <c>"hub"</c>, <c>"standalone"</c>.
|
||||
/// Populates OTel Resource <c>node.role</c>.
|
||||
/// </summary>
|
||||
public string? NodeRole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// App-specific Meter names to register with the OTel MeterProvider. Standard instrumentation
|
||||
/// meters are added automatically (ASP.NET Core, HttpClient, runtime, process).
|
||||
/// </summary>
|
||||
public string[] Meters { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// App-specific ActivitySource names to register with the OTel TracerProvider.
|
||||
/// </summary>
|
||||
public string[] ActivitySources { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Export path. Default Prometheus; use <see cref="ZbExporter.Otlp"/> for a real collector.
|
||||
/// </summary>
|
||||
public ZbExporter Exporter { get; set; } = ZbExporter.Prometheus;
|
||||
|
||||
/// <summary>
|
||||
/// Required when <see cref="Exporter"/> = <see cref="ZbExporter.Otlp"/>.
|
||||
/// OTLP gRPC endpoint, e.g. <c>"http://collector:4317"</c>.
|
||||
/// </summary>
|
||||
public string? OtlpEndpoint { get; set; }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user