nblumhardt / serilog-sinks-browserhttp

Serilog sink for client-side Blazor, with matching ASP.NET Core server relay
https://serilog.net
Apache License 2.0
52 stars 22 forks source link

Question - Adding Logged in User to Every Log Entry? #14

Closed benhysell closed 3 years ago

benhysell commented 3 years ago

Is there an easy way to add the currently logged in user to every log entry?

On the server project I have a middleware where I augment every log entry, I'm not seeing how to accomplish the same with client side Blazor.

In a perfect world:

Feels like I'm missing something simple here, but I'm not finding the breadcrumbs/hooks to get this done. Thanks!

nblumhardt commented 3 years ago

Hi @benhysell - an ILogEventEnricher and plugged into Serilog via Enrich.With() could be a useful starting point. Might be a good one to write up and post to Stack Overflow, seems like there may be a few ways to approach it. HTH!

benhysell commented 3 years ago

@nblumhardt Thanks for the suggestion!

tldr - Override logger creation and add a custom Enricher.

Sources:

Program.cs

builder.Services.AddSingleton<UserNameEnricher>();
builder.Logging.ClearProviders();
builder.Services.AddOptions();
builder.Services.AddSingleton<ILoggerFactory, LoggerFactory>();
builder.Services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
builder.Services.AddSingleton<ILoggerProvider, SerilogLoggerProvider>(p => new SerilogLoggerProvider(new LoggerConfiguration()
                .MinimumLevel.ControlledBy(levelSwitch)                              
                .Enrich.With(p.GetService<UserNameEnricher>())
                .Enrich.WithProperty("InstanceId", Guid.NewGuid().ToString("n"))
                .Enrich.FromLogContext()
                .WriteTo.BrowserConsole()
                .WriteTo.BrowserHttp($"{builder.HostEnvironment.BaseAddress}ingest", controlLevelSwitch: levelSwitch)
                .CreateLogger(), true));

UserNameEnricher.cs

 public class UserNameEnricher : ILogEventEnricher
    {
        private AuthenticationStateProvider _authenticationStateProvider;

        public UserNameEnricher(AuthenticationStateProvider authenticationStateProvider)
        {
            _authenticationStateProvider = authenticationStateProvider;
        }

        public async void Enrich(LogEvent logEvent, ILogEventPropertyFactory factory)
        {
            var user = await _authenticationStateProvider.GetAuthenticationStateAsync();            
            if (!(user?.User?.Identity?.IsAuthenticated ?? false))
                return;

            // Access the name of the logged-in user
            var userName = user.GetUserDisplayName(); //
            var userNameProperty = factory.CreateProperty("UserName", userName);
            logEvent.AddPropertyIfAbsent(userNameProperty);
        }
    }

/// <summary>
/// Get user display name
/// </summary>
public static string GetUserDisplayName(this AuthenticationState state)
{
    return state.GetClaim(ClaimTypes.Name);
}