Home > other >  How to format all datetime entries using ExpressionTemplate in Serilog?
How to format all datetime entries using ExpressionTemplate in Serilog?

Time:01-12

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);

  • Related