Introduction
Learn how to split log data using Serilog FilterExpression for any project type, from console to ASP.NET Core to a folder for the current day.
Also included are timing application operations using the NuGet package SerilogTimings.
Example project NuGet packages
The following NuGet packages are used. Some packages are deprecated, as for Serilog.Filters.Expressions.
Scenario
The objective is to write any class implementing IPerson to a separate file from other application operations.
public interface IPerson : IFormattable
{
private static readonly ILogger Logger = Log.ForContext("Category", nameof(IPerson));
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
DateOnly BirthDate { get; set; }
Address Address { get; set; }
public void LogDetails()
{
Logger.Information("Person First: {firstName}, Last: {lastName}", FirstName, LastName);
}
}
Each class implementing IPerson can call LogDetails to write to a log file for only IPerson, the configuration of which is shown shortly. Any class that implements IPerson can override LogDetails as shown in Citizen.
public class Citizen : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateOnly BirthDate { get; set; }
public DateOnly Since { get; set; }
public Address Address { get; set; }
string IFormattable.ToString(string format, IFormatProvider formatProvider)
=> $"{FirstName} {LastName}";
private static readonly ILogger Logger =
Log.ForContext("Category", nameof(IPerson));
public void LogDetails()
{
Logger.Information($"{Id,-5}{FirstName,-15}{LastName,-15}" +
$"{BirthDate,-12:MM/dd/yyyy}" +
$"{Since,-12:MM/dd/yyyy}{Address.Country}");
}
}
The Person class uses the default for LogDetails from IPerson interface.
public class Person : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateOnly BirthDate { get; set; }
public Address Address { get; set; }
string IFormattable.ToString(string format, IFormatProvider formatProvider)
=> $"{FirstName} {LastName}";
}
Serilog setup
The first WriteTo section filters out any class that implements IPerson using Filter.ByExcluding while the second WriteTo section indicates to only write classes that implements IPerson using Filter.ByIncludingOnly.
Each day the application runs, a new folder with the current date is created. For general logging, log.txt is created, and for operations for IPerson, a file named Person.txt is created.
Sample
In the following example, List people = MockedData.List(); contains a mix of Person and Citizen classes. The method LogPeopleDetailsWithTiming implements Serilog timing, while no Serilog timing is used in the method LogPeopleDetail.
namespace SerilogFiltering;
internal partial class Program
{
static void Main(string[] args)
{
// writes to log.txt
Log.Information("Start of code sample");
List<IPerson> people = MockedData.List();
if (UseSerilogTiming())
{
LogPeopleDetailsWithTiming(people);
}
else
{
LogPeopleDetail(people);
}
Log.Information("End of code sample");
Log.CloseAndFlush();
AnsiConsole.MarkupLine("[yellow]Done[/]");
Console.ReadLine();
}
private static void LogPeopleDetailsWithTiming(List<IPerson> people)
{
using var op = Operation.Begin("Iteration over people list");
foreach (var (index, person) in people.Index())
{
Console.WriteLine($"{index,-5}{person}");
// writes to person.txt
person.LogDetails();
}
op.Complete();
}
private static void LogPeopleDetail(List<IPerson> people)
{
foreach (var (index, person) in people.Index())
{
Console.WriteLine($"{index,-5}{person}");
// writes to person.txt
person.LogDetails();
}
}
}
Log output from LogPeopleDetailsWithTiming.
Log.txt
[2025-02-09 17:13:47.904] [Information] Start of code sample
[2025-02-09 17:13:48.174] [Information] Iteration over people list "completed" in 9.7 ms
[2025-02-09 17:13:48.182] [Information] End of code sample
Person.txt
[2025-02-09 17:13:48.170] [Information] 1 John Doe 12/01/1790 US
[2025-02-09 17:13:48.172] [Information] 2 Anne Doe 01/11/1969 01/01/2020 CA
[2025-02-09 17:13:48.172] [Information] 3 John Doe 12/01/1990 MG
[2025-02-09 17:13:48.173] [Information] 4 Jane Smith 05/15/1980 06/01/2010 US
[2025-02-09 17:13:48.173] [Information] 5 Alice Johnson 03/22/2000 UK
[2025-02-09 17:13:48.173] [Information] 6 Bob Brown 07/30/1975 08/01/2015 AU
[2025-02-09 17:13:48.173] [Information] 7 Charlie Davis 11/05/1995 NZ
[2025-02-09 17:13:48.173] [Information] 8 Diana Evans 09/12/1985 10/01/2018 ZA
[2025-02-09 17:13:48.173] [Information] 9 Eve Foster 04/18/1992 IN
[2025-02-09 17:13:48.173] [Information] 10 Frank Green 02/25/1970 03/01/2012 BR
[2025-02-09 17:13:48.173] [Information] 11 Grace Harris 06/14/1988 JP
[2025-02-09 17:13:48.174] [Information] 12 Henry Irvine 08/21/1998 09/01/2021 DE
Log output from LogPeopleDetail.
Log.txt
[2025-02-09 17:22:52.444] [Information] Start of code sample
[2025-02-09 17:22:52.609] [Information] End of code sample
Person.txt, the same output as above.
Controlling Serilog timing
This is done using a setting in appsettings.json.
"ApplicationSettings": {
"UseSerilogTiming": false
}
Model
public class ApplicationSettings
{
public bool UseSerilogTiming { get; set; }
}
The following code gets the setting for timing from appsettings.json
internal partial class Program
{
private static bool UseSerilogTiming()
=> ConfigurationRoot().GetSection("ApplicationSettings").Get<ApplicationSettings>().UseSerilogTiming;
private static IConfigurationRoot ConfigurationRoot() =>
new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
}
Calling logging setup
Using ModuleInitializer will call the setup as the first call of the console project.
internal partial class Program
{
[ModuleInitializer]
public static void Init()
{
SetupLogging.Development();
Console.Title = "Serilog Code sample";
WindowUtility.SetConsoleWindowPosition(WindowUtility.AnchorWindow.Center);
}
}
Summary
With the information provided, a developer can directly log into separate files using Serilog expressions, a convenient way to time code execution. What works in the presented console project will also work in an ASP.NET Core project.
Provided code
The source code is well documented using Jetbrain's AI Assistant.
Top comments (0)