Search code examples
asp.net-coreasp.net-core-webapiserilogappsettings

Username in Serilog file name in ASP.NET Core


I have an ASP.NET Core Web API. I need to add the username to the Serilog file name. My appsettings.json file looks like this:

"Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information"
      }
    },
    "WriteTo": [
      {       
        "Name": "File",
        "Args": {       
          "Path": "E:\\WebSite\\Development\\HealthAPI\\Logs\\log.txt",
          "rollingInterval": "Hour",
          "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {CorrelationId} {Level:u3}] [{Username}] {Message:lj}{NewLine}{Exception}"
        }
      }
    ]
  }

and this is program.cs:

var _logger = new LoggerConfiguration()
     .ReadFrom.Configuration(builder.Configuration)
     .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
     .Enrich.FromLogContext()
     .CreateLogger();
builder.Logging.AddSerilog(_logger);

app.UseMiddleware<LogUserNameMiddleware>();
app.UseSerilogRequestLogging();

My middleware is

public class LogUserNameMiddleware
{
    private readonly RequestDelegate next;

    public LogUserNameMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        using (LogContext.PushProperty("UserName", context.User.Identity.Name ?? "anonymous"))
        {
            await next(context);
        }
    }
}

I need user name in the filename like Log_ABC_Date.txt.

Update 1 : I changed Program.cs :

  var _logger = new LoggerConfiguration()
 .WriteTo.Map("LoginController", "Other", (name, wt) => wt.File(@"E:\Logs\log-{name}.txt"))
 .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
 .Enrich.FromLogContext()
 .CreateLogger();

And my controller :

 private readonly Serilog.ILogger _logger = Log.Logger.ForContext("LoginController","Username");

 _logger.Warning("Helllo !! {Name}",user.UserName);

Still this is giving issue. As Serilog.Sinks.Map is built around a mapping function, and as such, isn't able to be configured using JSON so I used Program.cs to configure Serilog.


Solution

  • According to Nicholas Blumhardt's suggestion, I implement it, please check below.

    1. Project Structure

    enter image description here

    2. LogUserNameMiddleware.cs

    using Serilog.Context;
    
    namespace LogUserName1
    {
        public class LogUserNameMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger<LogUserNameMiddleware> _logger;
    
            public LogUserNameMiddleware(RequestDelegate next, ILogger<LogUserNameMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {
                var userName = context.User.Identity.Name ?? "anonymous";
                using (LogContext.PushProperty("UserName", userName))
                {
                    await _next(context);
                }
            }
        }
    
    }
    

    3. Program.cs

    using Microsoft.AspNetCore.Authentication.Negotiate;
    using Serilog.Events;
    using Serilog;
    using LogUserName1;
    
    var builder = WebApplication.CreateBuilder(args);
    
    var _logger = new LoggerConfiguration()
        .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Information)
        .Enrich.FromLogContext()
        .WriteTo.Map(
            keyPropertyName: "UserName",
            defaultKey: "anonymous",
            (userName, logConfiguration) => logConfiguration.File($"E:\\WebSite\\Development\\HealthAPI\\Logs\\log_{userName}_{DateTime.Now:yyyy-MM-dd}.txt")
        )
        .CreateLogger();
    
    builder.Logging.AddSerilog(_logger);
    
    builder.Host.UseSerilog(_logger);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews();
    
    builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
       .AddNegotiate();
    
    builder.Services.AddAuthorization(options =>
    {
        // By default, all incoming requests will be authorized according to the default policy.
        options.FallbackPolicy = options.DefaultPolicy;
    });
    builder.Services.AddRazorPages();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    app.UseMiddleware<LogUserNameMiddleware>();
    app.UseSerilogRequestLogging();
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
    

    4. Packages

    enter image description here

    5. Test Result

    enter image description here