Destructure.ByTransforming<System.Type>好像没有生效

Destructure.ByTransforming<System.Type> does not appear to take effect

我试图通过仅使用类型名称、无命名空间来解构 System.Type,但得到了意外的结果。

设置

第一个转换按预期工作,生成短字符串(Guid 的前 8 个字符)。然而,第二个从未被调用:

.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

此外,控制台输出中的完全限定名称为绿色。我注意到 System.Uri 也是这种情况,想知道 Serilog 是否特别对待这些类型。

我能做些什么来影响 System.Type 的解构?

有两个问题阻止了您预期的行为。

首先,Destructure.ByTransforming<T>()对于T是不变的,但是在运行时你会发现你遇到的“真正的”System.Type对象是像RuntimeType这样的子类.

适用于 System.Type 及其子类的自定义 IDestructuringPolicy 可以解决此问题。

但是,其次,不幸的是 - Serilog 已经默认插入其中之一:

https://github.com/serilog/serilog/blob/dev/src/Serilog/Policies/ReflectionTypesScalarDestructuringPolicy.cs

因为 System.Type 可以生成如此巨大的对象图,并且由于像上面提到的 RuntimeType 陷阱这样的各种 footguns,Serilog 很早就禁用了 System.Type 和各种其他的结构化捕获反射值。

为了解决这个问题,您需要一个 ILogEventEnricher 来检测 属性 匹配 ScalarValue { Value: System.Type } 的值并将它们替换为修改后的 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);
            }
        }
    }
}

如果您只关心格式设置,Serilog.Expressions 可能还会提供一些选项。