如何在Serilog ExpressionTemplate中用#each输出列表属性JSON
Serilog ExpressionTemplate 自定义JSON日志时遍历列表子属性报错的解决方法
问题描述
尝试使用Serilog ExpressionTemplate生成自定义JSON日志,希望输出List参数的子属性,于是在模板中使用#each循环遍历列表项并输出其属性,但运行时抛出`System.ArgumentException: Syntax error (line 1, column 103): unexpected ``#```。若注释掉循环部分,日志仅输出列表项的对象类型名。
代码示例
using Serilog; using Serilog.Debugging; using Serilog.Templates; namespace SerilogExpressionTemplateObjectExpansion { internal class Program { static void Main(string[] args) { SelfLog.Enable(Console.Error); using var log = new LoggerConfiguration() .Enrich.WithProperty("Application", "Example") .WriteTo.Console(new ExpressionTemplate( "{ {@t: UtcDateTime(@t), @mt, @l: if @l = 'Information' then undefined() else @l, " + " test_results: [" + " {#each result in @p.test_results}" + " { name: result.Name, isInStock: {result.IsInStock} }," + " {#end}" + " ]" + " @x, ..@p} }\n")) .CreateLogger(); var sut = new SomeListOfItems { Items = new List<SomeItem> { new SomeItem { Name = "Tea", IsInStock = true }, new SomeItem { Name = "Coffee", IsInStock = false } } }; log.ForContext<Program>() .Warning("List of results {test_results}", sut.Items); } } internal class SomeItem { public string Name { get; set; } public bool IsInStock { get; set; } } internal class SomeListOfItems { public List<SomeItem> Items { get; set; } = new List<SomeItem>(); } }
注释循环后的输出
{"@t":"2024-09-19T14:22:47.0555253Z","@mt":"List of results {test_results}","@l":"Warning","test_results":["SerilogExpressionTemplateObjectExpansion.SomeItem","SerilogExpressionTemplateObjectExpansion.SomeItem"],"SourceContext":"SerilogExpressionTemplateObjectExpansion.Program","Application":"Example"}
错误原因
误用了模板语法:Serilog ExpressionTemplate采用Serilog表达式语言,并不支持#each这类类似Handlebars的循环语法,这是触发语法错误的直接原因。默认情况下,Serilog会将复杂对象序列化为类型名称,因此注释循环后仅能看到类的全名。
解决方案
要正确遍历列表并输出子属性,需使用Serilog表达式的enumerate()函数,结合投影语法生成结构化列表内容。修改ExpressionTemplate的定义,替换掉原有的#each循环:
修改后的核心代码
using var log = new LoggerConfiguration() .Enrich.WithProperty("Application", "Example") .WriteTo.Console(new ExpressionTemplate( "{ {@t: UtcDateTime(@t), @mt, @l: if @l = 'Information' then undefined() else @l, " + " test_results: enumerate(@p.test_results, x => { name: x.Name, isInStock: x.IsInStock })," + " @x, ..@p} }\n")) .CreateLogger();
说明
enumerate(collection, projection):遍历集合中的每个元素,通过第二个参数的投影函数生成新的结构化对象- 投影函数中直接访问元素的子属性(
x.Name、x.IsInStock),即可生成包含子属性的JSON结构 - 移除原循环中多余的逗号,避免JSON格式错误
正确输出示例
{"@t":"2024-09-19T14:22:47.0555253Z","@mt":"List of results {test_results}","@l":"Warning","test_results":[{"name":"Tea","isInStock":true},{"name":"Coffee","isInStock":false}],"SourceContext":"SerilogExpressionTemplateObjectExpansion.Program","Application":"Example"}
内容的提问来源于stack exchange,提问作者AndyCW
相关产品推荐
相关产品推荐

