Files
scadaproj/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry.Serilog/ILogRedactor.cs
T
Joseph Doherty 05cc62aab3 Implement nested log redaction (Telemetry-002)
RedactionEnricher now projects each property into a mutable view the ILogRedactor
can edit: scalars stay as their CLR value, while StructureValue/SequenceValue/
DictionaryValue become nested IDictionary<string,object?>/IList<object?> the
redactor descends into recursively. A field nested inside a destructured {@Object}
can now be masked or removed — closing the gap documented as a limitation.

- Project/Rebuild round-trip preserves StructureValue.TypeTag and original
  dictionary keys; redactor-synthesised plain dicts/lists are rebuilt too.
- Untouched properties are not reallocated: structural ValueEquals skips write-back
  unless a property actually changed. Scalar fast path and no-redactor/no-property
  short-circuits retained.
- +5 nested-reach tests (mask/remove a field, sequence element, dictionary value,
  two-levels-deep); the old 'cannot reach' limitation test replaced. Serilog 34, 0 warnings.
- ILogRedactor XML doc + library README updated to document the recursive reach.
2026-06-01 12:12:26 -04:00

32 lines
2.0 KiB
C#

namespace ZB.MOM.WW.Telemetry.Serilog;
/// <summary>
/// Seam for project-specific log-event redaction. The shared library applies this via
/// <see cref="RedactionEnricher"/>; each project provides its own implementation that knows which
/// fields (by property name) or which command payloads must not leave the process in log events.
/// If no <see cref="ILogRedactor"/> is registered in DI, <see cref="RedactionEnricher"/> is a no-op.
/// </summary>
public interface ILogRedactor
{
/// <summary>
/// Inspects and mutates the supplied log-event <paramref name="properties"/> in place — remove
/// or replace any sensitive values. Called on every log event before it reaches any sink.
/// Both removing a key (the property is dropped from the event) and replacing its value are
/// honoured by <see cref="RedactionEnricher"/>.
/// <para>
/// <b>Reach — top-level and nested.</b> A scalar property (e.g. <c>{apiKey}</c>) appears as its
/// unwrapped CLR value, which you can read and replace directly. A <b>destructured</b> property is
/// projected into a mutable view you can descend into: a <c>{@Object}</c> arrives as an
/// <c>IDictionary&lt;string, object?&gt;</c> of its fields, a logged collection as an
/// <c>IList&lt;object?&gt;</c>, and a logged dictionary as an <c>IDictionary&lt;string, object?&gt;</c>
/// keyed by the string form of each key — all recursively. You can therefore mask or remove a field
/// nested inside a destructured object, for example:
/// <code>if (properties["command"] is IDictionary&lt;string, object?&gt; command) command["apiKey"] = "***";</code>
/// The structure's type tag and a dictionary's original keys are preserved when the event is
/// rebuilt, and properties you do not touch are left intact.
/// </para>
/// </summary>
/// <param name="properties">The mutable property dictionary for the current log event.</param>
void Redact(IDictionary<string, object?> properties);
}