I am attempting to destructure System.Type
by using just the type name, sans namespace, but am getting an unexpected result.
The first transformation works as expected, producing the short string (the first 8 characters of a Guid
). However, the second is never called:
.Destructure.ByTransforming<Id>(id => id.ToShortString())
.Destructure.ByTransforming<Type>(type => UseUnqualifiedName(type) ? type.Name : type.ToString())
_logger.LogInfo("Instance loaded: {@Type}.{@Id}", typeof(MyProject.MyNamespace.MyType), id);
[INF] Instance: MyType.64b8ac0d
[INF] Instance: MyProject.MyNamespace.MyType.64b8ac0d
In addition, the fully-qualified name is green in the console output. I noticed this is also the case with System.Uri
, and wonder if Serilog treats these types specially.
What can I do to affect the destructuring of System.Type
?
There are two problems preventing the behavior you expect.
First, Destructure.ByTransforming<T>()
is invariant for T
, but at runtime you'll find that the "real" System.Type
objects that you encounter are subclasses like RuntimeType
.
A custom IDestructuringPolicy
that applies to System.Type
and its subclasses can work around this.
But, second, unfortunately - Serilog already has one of these that's plugged in by default:
Because System.Type
can produce such enormous object graphs, and because of various footguns like the RuntimeType
gotcha mentioned above, Serilog very early on disabled structured capturing of System.Type
and various other reflection values.
To get around this, you need an ILogEventEnricher
that detects property value matching ScalarValue { Value: System.Type }
and replaces them with a modified ScalarValue
.
// `Enrich.With(new RecaptureTypeEnricher())`
class RecaptureTypeEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
List<LogEventProperty> updates = null;
foreach (var property in logEvent.Properties)
{
if (property.Value is ScalarValue { Value: Type type })
{
updates ??= new List<LogEventProperty>();
updates.Add(new LogEventProperty(property.Key, new ScalarValue(UseUnqualifiedName(type))));
}
}
if (updates != null)
{
foreach (var update in updates)
{
logEvent.AddOrUpdateProperty(update);
}
}
}
}
If you're just concerned about formatting, Serilog.Expressions might also offer up some options.