如何让Serilog解构运算符忽略空值属性?

问题描述 投票:0回答:1

有没有办法告诉 Serilog 不要序列化使用

@
(解构)运算符记录的对象的空值属性?我发现了一些关于该主题的帖子(实际上,我发现了更多,但这两个似乎与问题和答案更相关):

(1) 在 Serilog 中解构时忽略空值 是从 2019 年开始的,唯一的答案表明没有办法做到这一点。 (2) 忽略写入输出的 null 属性? 也是 2019 年的内容,但它表明可能有一种方法可以使用自定义

ILogEventEnricher
来做到这一点,但没有关于如何实际执行此操作的指导(它还指的是 json 格式化忽略 null 属性,l 但我不确定这篇文章是否指格式化对象或使用 JSON 格式化程序生成 JSON 格式的日志条目)。

我们目前正在使用带有

Console
格式化程序的
Serilog.Templates.ExpressionTemplate, Serilog.Expressions
接收器(我们将其与转义新行的自定义丰富器结合使用,因为 Azure 不喜欢日志条目中的新行),以及带有默认值的
File
接收器格式化程序。为了忽略输出中的空值,我们使用自定义扩展方法
ToJson()
,将对象序列化为简单字符串值(并且我们使用 JSON.NET):

public static string? ToJson
(
    this object data
)
{
    JsonSerializer json = new()
    {
        // Do not change the following line or it may fail to serialize hierarchical data.
        PreserveReferencesHandling = PreserveReferencesHandling.Objects, 
        NullValueHandling = NullValueHandling.Ignore,
        DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffZ",
        Formatting = Formatting.None,
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        TypeNameHandling = TypeNameHandling.None,
    };

    StringWriter textWriter = new();

    json.Serialize(textWriter, data);

    return textWriter.ToString();
}

所以,如果我们有一个像这样的对象:

{"propA":"valueA","propB":null,"propC":"valueC"}

我们将其记录为:

logger.LogDebug("Data: {data}", data.ToJson());

输出将是:

Data: {"propA":"valueA","propC":"valueC"}

但是如果我们记录为:

logger.LogDebug("Data: {@data}", data);

输出将是:

Data: {"propA":"valueA","propB":null,"propC":"valueC"}

我们如何使用

@
(解构)运算符进行日志记录而不序列化具有空值的属性?

c# serialization json.net serilog destructuring
1个回答
0
投票

我有类似的需求,但很惊讶这不是开箱即用的功能。让我想知道我是否看错了,但按照已关闭问题中的指导,我创建了一个新的格式化程序。我不想复制和修改现有的(然后不得不担心修补),而是倾向于从现有的派生。它使用私有成员让我认为开发人员没有考虑这种可能性。 🤷u200d♀️

这只是从结构中过滤掉空标量值,我认为它可以满足您的要求。当然,性能会受到一点影响,但应该不会太糟糕。

namespace Serilog.Formatting.Compact
{
    public class CompactJsonExtantFormatter : CompactJsonFormatter
    {
        public CompactJsonExtantFormatter(JsonValueFormatter valueFormatter = null) :
            base(valueFormatter ?? new JsonExtantValueFormatter(typeTagName: "$type"))
        { }
    }
    public class JsonExtantValueFormatter : JsonValueFormatter
    {
        public JsonExtantValueFormatter(string typeTagName) :
            base(typeTagName)
        { }

        protected override bool VisitStructureValue(TextWriter state, StructureValue structure)
        {
            List<LogEventProperty> extantProperties = new List<LogEventProperty>();
            foreach (var property in structure.Properties)
            {
                if (!(property.Value is ScalarValue scalarValue) || !(scalarValue.Value is null))
                {
                    extantProperties.Add(property);
                }
            }

            if (extantProperties.Count == structure.Properties.Count)
            {
                return base.VisitStructureValue(state, structure);
            }

            return base.VisitStructureValue(state, new StructureValue(extantProperties));
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.