I'm currently using ExpressionTemplate for custom log formatting, but can't find proper way to format all entries with datetime type.
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, ..@p} }\n"));
});
...
using(var scope = app.Services.CreateScope())
{
var logger = scope.ServiceProvider.GetService<ILogger<Program>>();
logger.LogInformation("{@Now}, {@UtcNow}", DateTime.Now, DateTime.UtcNow);
}
//{"Time":"2023-01-12 02:48:35.44","@mt":"{@Now}, {@UtcNow}","@l":"Information","Now":"2023-01-12T11:48:35.0203770 09:00","UtcNow":"2023-01-12T02:48:35.4406010Z","SourceContext":"Program"}
As you see, 'Time' is properly formatted using ToString, but the others are not,
I want it to be like..
{"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"}
I tried IFormatProvider to fix this,
var builder = WebApplication.CreateBuilder(args);
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, ..@p} }\n", new CustomDateFormat()));
});
public class CustomDateFormat : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string fmt, object arg, IFormatProvider formatProvider)
{
if (arg is DateTime dt) return dt.ToString("yyyy-MM-dd HH:mm:ss.ff");
if (arg is DateOnly dateOnly) return dateOnly.ToString("yyyy-MM-dd");
if (arg is TimeOnly timeOnly) return timeOnly.ToString("HH:mm:ss.fff");
//is timespan
return ((TimeSpan)arg).ToString();
}
}
But results were same. How can I format all datetime entries using ExpressionTemplate?
CodePudding user response:
You can use ILogEventEnricher
and create your own DateTimeFormatter
Enricher.
public class DateTimeFormatter : ILogEventEnricher
{
private readonly string _dateTimeFormat;
public DateTimeFormatter(string dateTimeFormat)
{
_dateTimeFormat = dateTimeFormat;
}
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var properties = logEvent.Properties.ToList();
foreach (var property in properties)
{
if (property.Value is ScalarValue scalarValue && scalarValue.Value is DateTime dateTime)
{
logEvent.RemovePropertyIfPresent(property.Key);
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(property.Key, dateTime.ToString(_dateTimeFormat)));
}
}
}
}
And pass the DateTimeFormatter
in the LoggerConfiguration
like this.
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration.ReadFrom.Configuration(context.Configuration);
configuration.ReadFrom.Services(services);
configuration.Enrich.FromLogContext();
configuration.Enrich.With(new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"));
configuration.WriteTo.Console();
});
logger.Information("Now: {@Now}, UtcNow: {@UtcNow}", DateTime.Now, DateTime.UtcNow);