refactor(telemetry.serilog): review fixes (thread-safe redactor, bootstrap logger, minlevel ordering, test coverage)

This commit is contained in:
Joseph Doherty
2026-06-01 07:48:57 -04:00
parent 37fb84f477
commit f1240c0bd4
7 changed files with 140 additions and 48 deletions
@@ -43,4 +43,33 @@ public sealed class EnricherTests
Environment.MachineName,
ScalarValue(logEvent, ZbLogEnricherNames.NodeHostname));
}
[Fact]
public void Null_SiteId_and_NodeRole_are_suppressed_but_NodeHostname_is_always_present()
{
var sink = new InMemorySink();
var options = new ZbTelemetryOptions
{
ServiceName = "otopcua",
SiteId = null,
NodeRole = null,
};
var loggerConfig = new LoggerConfiguration();
ZbSerilogConfig.Apply(loggerConfig, options);
using var logger = loggerConfig
.WriteTo.Sink(sink)
.CreateLogger();
logger.Information("hello");
var logEvent = Assert.Single(sink.LogEvents);
Assert.False(logEvent.Properties.ContainsKey(ZbLogEnricherNames.SiteId),
"SiteId should be absent when null");
Assert.False(logEvent.Properties.ContainsKey(ZbLogEnricherNames.NodeRole),
"NodeRole should be absent when null");
Assert.Equal(
Environment.MachineName,
ScalarValue(logEvent, ZbLogEnricherNames.NodeHostname));
}
}
@@ -84,7 +84,53 @@ public sealed class RedactionTests
using var host = builder.Build();
// Serilog.ILogger is registered by AddSerilog — not Microsoft.Extensions.Logging.ILogger.
var logger = host.Services.GetRequiredService<ILogger>();
logger.Information("otlp wiring smoke test");
}
[Fact]
public void BuildResourceAttributes_contains_required_keys_and_optional_keys_when_set()
{
var options = new ZbTelemetryOptions
{
ServiceName = "mxgateway",
ServiceNamespace = "ZB.MOM.WW",
SiteId = "site-a",
NodeRole = "central",
};
var attributes = ZbSerilogConfig.BuildResourceAttributes(options);
// Required keys always present.
Assert.True(attributes.ContainsKey("service.name"), "service.name must be present");
Assert.True(attributes.ContainsKey("service.namespace"), "service.namespace must be present");
Assert.True(attributes.ContainsKey("host.name"), "host.name must be present");
// Optional keys present when options supply them.
Assert.True(attributes.ContainsKey("site.id"), "site.id must be present when SiteId is set");
Assert.True(attributes.ContainsKey("node.role"), "node.role must be present when NodeRole is set");
Assert.Equal("mxgateway", attributes["service.name"]);
Assert.Equal("ZB.MOM.WW", attributes["service.namespace"]);
Assert.Equal(Environment.MachineName, attributes["host.name"]);
Assert.Equal("site-a", attributes["site.id"]);
Assert.Equal("central", attributes["node.role"]);
}
[Fact]
public void BuildResourceAttributes_omits_optional_keys_when_not_set()
{
var options = new ZbTelemetryOptions
{
ServiceName = "mxgateway",
SiteId = null,
NodeRole = null,
};
var attributes = ZbSerilogConfig.BuildResourceAttributes(options);
Assert.False(attributes.ContainsKey("site.id"), "site.id must be absent when SiteId is null");
Assert.False(attributes.ContainsKey("node.role"), "node.role must be absent when NodeRole is null");
}
}
@@ -42,11 +42,16 @@ public sealed class TraceContextEnricherTests
Assert.NotNull(activity);
Assert.NotNull(Activity.Current);
// Capture IDs before the log call so assertions are not sensitive to activity
// lifecycle — Activity.Current may differ after the log call returns.
var expectedTraceId = activity.TraceId.ToString();
var expectedSpanId = activity.SpanId.ToString();
logger.Information("traced");
var logEvent = Assert.Single(sink.LogEvents);
Assert.Equal(Activity.Current!.TraceId.ToString(), ScalarOrNull(logEvent, "trace_id"));
Assert.Equal(Activity.Current!.SpanId.ToString(), ScalarOrNull(logEvent, "span_id"));
Assert.Equal(expectedTraceId, ScalarOrNull(logEvent, "trace_id"));
Assert.Equal(expectedSpanId, ScalarOrNull(logEvent, "span_id"));
}
[Fact]