如何在Serilog中使用ExpressionTemplate格式化所有日期时间条目?
The issue you're facing is that the ..@p (property expansion) in ExpressionTemplate doesn't automatically apply your custom IFormatProvider to DateTime properties—it uses Serilog's default serialization logic instead. To fix this, you need to explicitly handle DateTime/DateTimeOffset (and other date-related types) directly in your template by iterating over properties and formatting them conditionally.
Solution: Conditional Formatting in the ExpressionTemplate
Replace your existing template with one that loops through all properties in @p, checks their type, and formats date/time values accordingly. Here's how to do it:
builder.Host.UseSerilog((context, services, configuration) => { configuration.ReadFrom.Configuration(context.Configuration); configuration.ReadFrom.Services(services); configuration.Enrich.FromLogContext(); configuration.WriteTo.Console(new ExpressionTemplate(@" { Time: ToString(UtcDateTime(@t), 'yyyy-MM-dd HH:mm:ss.ff'), @mt, @r, @l, @x, ..[for prop in @p: { [prop.Key]: prop.Value is DateTime dt ? ToString(dt, 'yyyy-MM-dd HH:mm:ss.ff') : prop.Value is DateTimeOffset dto ? ToString(dto.UtcDateTime, 'yyyy-MM-dd HH:mm:ss.ff') : prop.Value is DateOnly d ? ToString(d, 'yyyy-MM-dd') : prop.Value is TimeOnly t ? ToString(t, 'HH:mm:ss.fff') : prop.Value } ] } ")); });
Why Your Original IFormatProvider Didn't Work
The IFormatProvider you created is only used when you explicitly call ToString() in the template (like you did for the Time field). It doesn't automatically apply to properties expanded via ..@p, since Serilog's default property serialization bypasses this formatter for nested/expanded properties.
Test Output
When you run your logger code:
using(var scope = app.Services.CreateScope()) { var logger = scope.ServiceProvider.GetService<ILogger<Program>>(); logger.LogInformation("{@Now}, {@UtcNow}", DateTime.Now, DateTime.UtcNow); }
You'll get the desired formatted output:
{"Time":"2023-01-12 02:48:35.44","@mt":"{@Now}, {@UtcNow}","@l":"Information","Now":"2023-01-12 02:48:35.44","UtcNow":"2023-01-12 02:48:35.44","SourceContext":"Program"}
Notes
- The template uses a loop (
[for prop in @p:]) to iterate over all properties in@p. - It checks for multiple date/time types (
DateTime,DateTimeOffset,DateOnly,TimeOnly) and applies your custom format to each. - Non-date properties are output as-is, preserving their original values.
内容的提问来源于stack exchange,提问作者user994514

