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.
This commit is contained in:
@@ -14,16 +14,16 @@ public interface ILogRedactor
|
||||
/// Both removing a key (the property is dropped from the event) and replacing its value are
|
||||
/// honoured by <see cref="RedactionEnricher"/>.
|
||||
/// <para>
|
||||
/// <b>Reach — scalar top-level properties only.</b> Each entry's value is the unwrapped scalar
|
||||
/// of a Serilog <c>ScalarValue</c> property (so simple string/number/etc. properties such as
|
||||
/// <c>{apiKey}</c> can be read and masked directly). <b>Destructured / structured properties are
|
||||
/// not unwrapped:</b> a <c>{@Object}</c> property arrives as the raw Serilog
|
||||
/// <c>StructureValue</c> wrapper (and a sequence/dictionary as <c>SequenceValue</c>/
|
||||
/// <c>DictionaryValue</c>). A redactor can therefore replace or remove the <i>whole</i>
|
||||
/// top-level property, but it cannot reach a field <i>nested inside</i> a destructured object to
|
||||
/// mask it selectively. To protect a sensitive field of a logged object, do not destructure it
|
||||
/// (log the field as its own scalar property), or remove/replace the entire structured property
|
||||
/// by key.
|
||||
/// <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<string, object?></c> of its fields, a logged collection as an
|
||||
/// <c>IList<object?></c>, and a logged dictionary as an <c>IDictionary<string, object?></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<string, object?> 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>
|
||||
|
||||
Reference in New Issue
Block a user