最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

.net - Extending logger to include user data - Stack Overflow

programmeradmin5浏览0评论

I currently use Serilog with the Ilogger extension where I log information in my core api.

As part of a requirement all logs that have a user registered must include the UserId in its logging.

Currently the user Id is taken from the httpcontext which is injected into a UserContextService.

I now want to extend Serilog or create a Ilogger wrapper which would then pass in my IUserContextService and apply the UserId as part of the logger information.

Here is the code for some context on what I currently do at the moment..

public class UserContextService : IUserContextService
{
private readonly IHttpContextAccessor? _accessor;

public UserContextService(IHttpContextAccessor accessor)
{
    _accessor = accessor;
}

public AuthUser GetUserDetails()
{
    var userContext = new AuthUser
    {
        Id = _accessor?.HttpContext?.Request?.HttpContext?
        .User?.Claims?.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value
    };

    return userContext;
}

calling code

   private readonly ILogger<MyService> _logger;

 string? userId = _userContextService.GetUserDetails().Id;
_logger.LogWarning("my warning message: {userId}", userId);

I currently use Serilog with the Ilogger extension where I log information in my core api.

As part of a requirement all logs that have a user registered must include the UserId in its logging.

Currently the user Id is taken from the httpcontext which is injected into a UserContextService.

I now want to extend Serilog or create a Ilogger wrapper which would then pass in my IUserContextService and apply the UserId as part of the logger information.

Here is the code for some context on what I currently do at the moment..

public class UserContextService : IUserContextService
{
private readonly IHttpContextAccessor? _accessor;

public UserContextService(IHttpContextAccessor accessor)
{
    _accessor = accessor;
}

public AuthUser GetUserDetails()
{
    var userContext = new AuthUser
    {
        Id = _accessor?.HttpContext?.Request?.HttpContext?
        .User?.Claims?.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value
    };

    return userContext;
}

calling code

   private readonly ILogger<MyService> _logger;

 string? userId = _userContextService.GetUserDetails().Id;
_logger.LogWarning("my warning message: {userId}", userId);
Share Improve this question asked Feb 17 at 10:00 James Andrew SmithJames Andrew Smith 1,5682 gold badges23 silver badges43 bronze badges 1
  • Have you thought about putting a middleware in place after the authentication middleware, which would enrich the log context with the user ID? – mason Commented Feb 17 at 16:28
Add a comment  | 

1 Answer 1

Reset to default 2

I now want to extend Serilog or create a Ilogger wrapper which would then pass in my IUserContextService and apply the UserId as part of the logger information.

You can try to use the following methods:

Using Serilog Enrichers.

Serilog provides LogContext, which allows you to add properties to log events dynamically. You can create middleware that retrieves the UserId from IUserContextService and enriches the logging context. More detail information, see Pushing properties to the ILogger and LogContext.

    public class SerilogUserEnricherMiddleware
    {
        private readonly RequestDelegate _next;

        public SerilogUserEnricherMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context, IUserContextService userContextService)
        {
            var userId = userContextService.GetUserDetails().Id;
            if (!string.IsNullOrEmpty(userId))
            {
                using (LogContext.PushProperty("UserId", userId))
                {
                    await _next(context);
                }
            }
            else
            {
                await _next(context);
            }
        }
    }

And the AuthUser class as below:

    public class AuthUser
    {
        public string Id { get; set; }
    }

Then, in the Program.cs file register the middleware as below:

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] (UserId: {UserId}) {Message}{NewLine}{Exception}")
    .CreateLogger();

try
{
    Log.Information("Starting web application");
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddSerilog(); // <-- Add this line

    //...

    builder.Services.AddScoped<IUserContextService, UserContextService>();

    var app = builder.Build();

    app.UseMiddleware<SerilogUserEnricherMiddleware>();
    //... other middleware
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    app.MapRazorPages();

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}

After login success, we can see the user id from the log:

Using a Custom ILogger Wrapper

You can create a wrapper around ILogger to inject User Id:

public interface ICustomLogger
{
    void LogInformation(string message, params object[] args);
}

public class CustomLogger : ICustomLogger
{
    private readonly ILogger<CustomLogger> _logger;
    private readonly IUserContextService _userContextService;

    public CustomLogger(ILogger<CustomLogger> logger, IUserContextService userContextService)
    {
        _logger = logger;
        _userContextService = userContextService;
    }

    public void LogInformation(string message, params object[] args)
    {
        var userId = _userContextService.GetUserDetails().Id ?? "Unknown";
        _logger.LogInformation("UserId: {UserId} - " + message, userId, args);
    }
}

Register the Custom Logger:

builder.Services.AddScoped<ICustomLogger, CustomLogger>();

Finally, inject and use ICustomLogger instead of ILogger in your services/controllers.

public class HomeController : Controller
{
    private readonly ICustomLogger _customlogger;
    public HomeController(ICustomLogger customLogger)
    { 
        _customlogger = customLogger;
    } 
    public IActionResult Index()
    { 
        _customlogger.LogInformation("Custom logger information");
        return View();
    }

The result as below:

发布评论

评论列表(0)

  1. 暂无评论