DEV Community

Cover image for Serilog filter log files by type
Karen Payne
Karen Payne

Posted on

Serilog filter log files by type

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.

NET 9 Source code

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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}");
    }
}
Enter fullscreen mode Exit fullscreen mode

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}";
}
Enter fullscreen mode Exit fullscreen mode

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.

Serilog configuration method in the class SetupLogging

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

    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Person.txt, the same output as above.

Controlling Serilog timing

This is done using a setting in appsettings.json.

"ApplicationSettings": {
  "UseSerilogTiming": false
}
Enter fullscreen mode Exit fullscreen mode

Model

public class ApplicationSettings
{
    public bool UseSerilogTiming { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

See Also

Top comments (0)