Multiple filters for one logger with Serilog

15,561

Solution 1

Serilog will also do this as you describe, by filtering by namespace:

var isController = Matching.FromSource("MyApp.Controllers");
var isService = Matching.FromSource("MyApp.Services");

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .WriteTo.RollingFile("d:/logs/recon-api-all-{Date}.log")
    .WriteTo.Logger(l => l
        .Filter.ByIncludingOnly(isController)
        .WriteTo.RollingFile("d:/logs/recon-api-controller-{Date}.log"))
    .WriteTo.Logger(l => l
        .Filter.ByIncludingOnly(isService)
        .WriteTo.RollingFile("d:/logs/recon-api-service-{Date}.log"))
    .WriteTo.Logger(l => l
        .Filter.ByExcluding(e => isController(e) || iService(e))
        .WriteTo.RollingFile("d:/logs/recon-api-other-{Date}.log"))
    .CreateLogger();

If the controllers and services aren't identifiable by namespace, you can write a lambda function in place of isController or isService to identify them.

(Your scenario might be better suited to a logging format that permits easier filtering, so that you can selectively view controller events, service events and so-on by filtering after the fact. Check out the other Serilog provided sinks for some options.)

Solution 2

The question was already answered. but in case if you want to log different levels into different files you can use the following configurations:

var conn = configuration.GetSection("ConnectionStrings:SqlConn").Value;
var serilogLogger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.Console()
            .WriteTo.MSSqlServer(conn, new MSSqlServerSinkOptions { TableName = "ErrorLogs", AutoCreateSqlTable = true }, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Warning)
            .WriteTo.MSSqlServer(conn, new MSSqlServerSinkOptions { TableName = "InfoLogs", AutoCreateSqlTable = true }, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information)
            .CreateLogger();
Share:
15,561
Carsten Franke
Author by

Carsten Franke

Updated on June 21, 2022

Comments

  • Carsten Franke
    Carsten Franke about 2 years

    I am trying to setup Serilog with my ASP.NET Core application. I'd like to have one log file for all controllers, one for all services, one for the rest and ideally one which contains everything. Every controller is inheriting BaseController and every service BaseService. The controller and the service I am calling are writing a trace log event.

    The logger and the service are retrieved via dependy injection. The service looks like the controller (regarding the logger).

    public class UsersController: BaseController
    {
        private UserService service { get; }
    
        public UsersController(ILogger<UsersController> logger, UserService userService) : base(logger)
        {
            service = userService;
        }
    }
    
    public abstract class BaseController: Controller
    {
        protected readonly ILogger<BaseController> Logger;
    
        public BaseController(ILogger<BaseController> logger)
        {
            Logger = logger;
        }
    }
    

    Approach 1 (working with the base clases only)

    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Verbose()
        .WriteTo.Logger(l => l
            .MinimumLevel.Verbose()
    
            .WriteTo.Logger(l2 => l2
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<BaseController>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-controller-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<BaseService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-service-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByExcluding(Matching.FromSource<BaseController>())
                    .Filter.ByExcluding(Matching.FromSource<BaseService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-other-{Date}.log"))
            )
    
            .WriteTo.RollingFile("d:/logs/recon-api-all-{Date}.log"))
        .CreateLogger();
    

    This creates the log files for "other" and "all" only. Both contain the log events from controller and service.

    Approach 2 (working with the concrete classes)

    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Verbose()
        .WriteTo.Logger(l => l
            .MinimumLevel.Verbose()
    
            .WriteTo.Logger(l2 => l2
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<BaseController>())
                    .Filter.ByIncludingOnly(Matching.FromSource<PrivilegeGroupsController>())
                    .Filter.ByIncludingOnly(Matching.FromSource<PrivilegesController>())
                    .Filter.ByIncludingOnly(Matching.FromSource<UsersController>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-controller-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<BaseService>())
                    .Filter.ByIncludingOnly(Matching.FromSource<PrivilegeGroupService>())
                    .Filter.ByIncludingOnly(Matching.FromSource<PrivilegeService>())
                    .Filter.ByIncludingOnly(Matching.FromSource<UserService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-service-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByExcluding(Matching.FromSource<BaseController>())
                    .Filter.ByExcluding(Matching.FromSource<UsersController>())
                    .Filter.ByExcluding(Matching.FromSource<PrivilegeGroupsController>())
                    .Filter.ByExcluding(Matching.FromSource<PrivilegesController>())
                    .Filter.ByExcluding(Matching.FromSource<BaseService>())
                    .Filter.ByExcluding(Matching.FromSource<UserService>())
                    .Filter.ByExcluding(Matching.FromSource<PrivilegeGroupService>())
                    .Filter.ByExcluding(Matching.FromSource<PrivilegeService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-other-{Date}.log"))
            )
    
            .WriteTo.RollingFile("d:/logs/recon-api-all-{Date}.log"))
        .CreateLogger();
    

    This creates the log files for "other" and "all" only. "all" contains the log events from controller and service.

    Approach 3 (working with the user classes only)

    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Verbose()
        .WriteTo.Logger(l => l
            .MinimumLevel.Verbose()
    
            .WriteTo.Logger(l2 => l2
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<UsersController>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-controller-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByIncludingOnly(Matching.FromSource<UserService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-service-{Date}.log"))
                .WriteTo.Logger(l3 => l3
                    .Filter.ByExcluding(Matching.FromSource<UsersController>())
                    .Filter.ByExcluding(Matching.FromSource<UserService>())
                    .MinimumLevel.Verbose()
                    .WriteTo.RollingFile("d:/logs/recon-api-other-{Date}.log"))
            )
    
            .WriteTo.RollingFile("d:/logs/recon-api-all-{Date}.log"))
        .CreateLogger();
    

    This creates all the log files and every file contains the expected messages.

    What needs to be done to achieve the desired goal (see second sentence of this post).

    Best regards, Carsten

  • Carsten Franke
    Carsten Franke almost 8 years
    So... back from vacation: Your suggestion behaves exactly like my first approach (it creates log files for "all" and "other" and both contain all the messages). Some code for sanity check: var isController = Matching.FromSource("ReconAPI.Controllers"); and namespace ReconAPI.Controllers
  • Nicholas Blumhardt
    Nicholas Blumhardt almost 8 years
    Placing a breakpoint in .Filter.ByExcluding(e => isController(e) || iService(e)) and examining the events might help get to the bottom of it. Are your events correctly tagged with SourceContext?
  • Carsten Franke
    Carsten Franke almost 8 years
    I was using Log.Logger only. Using Log.ForContext<T>()helped a lot and now the messages appear exactly where they should. Thanks a bunch!
  • Nicholas Blumhardt
    Nicholas Blumhardt almost 8 years
    Great. glad to hear it :-)
  • Idothisallday
    Idothisallday almost 6 years
    @NicholasBlumhardt how would you this method .Filter.ByExcluding() in json file to exclude microsoft logs?
  • CodeConstruct
    CodeConstruct over 5 years
    how can we implement this in appsetting.json. (Mainly filtering a specific class)