问题
I have this Serilog configuration in program.cs
public class Program
{
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.Build();
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.WriteTo.MSSqlServer(Configuration.GetConnectionString("DefaultConnection"), "dbo.Log")
.Enrich.WithThreadId()
.Enrich.WithProperty("Version", "1.0.0")
.CreateLogger();
try
{
BuildWebHost(args).Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
Now i want to add HttpContext.Current.User.Identity.Name
into all log messages.
I tried to create new Enrich class following documentation https://github.com/serilog/serilog/wiki/Configuration-Basics#enrichers
class UsernameEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, HttpContext httpContext)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
"Username", httpContext.User.Identity.Name));
}
}
But there is conflict with ILogEventEnricher which does not know HttpContext.
I also tried to install Nuget package Serilog.Web.Classic which contains Username Enricher, but there is conflict between target framework .Net Framework and .Net Core therefore i cannot use this plugin.
Any idea ?
回答1:
You can create a middleware to put required property to LogContext.
public class LogUserNameMiddleware
{
private readonly RequestDelegate next;
public LogUserNameMiddleware(RequestDelegate next)
{
this.next = next;
}
public Task Invoke(HttpContext context)
{
LogContext.PushProperty("UserName", context.User.Identity.Name);
return next(context);
}
}
Also you need to add the following to your logger configuration:
.Enrich.FromLogContext()
回答2:
An alternative to using middleware is to use an action filter.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Serilog.Context;
namespace Acme.Widgets.Infrastructure
{
public class LogEnrichmentFilter : IActionFilter
{
private readonly IHttpContextAccessor httpContextAccessor;
public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void OnActionExecuting(ActionExecutingContext context)
{
var httpUser = this.httpContextAccessor.HttpContext.User;
if (httpUser.Identity.IsAuthenticated)
{
var appUser = new AppIdentity(httpUser);
LogContext.PushProperty("Username", appUser.Username);
}
else
{
LogContext.PushProperty("Username", "-");
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do nothing
}
}
}
In your Startup.ConfigureServices
you will need to:
- Ensure
IHttpContextAccessor
is added to the IoC container - Add the
LogEnrichmentFilter
to the IoC container, scoped to the request - Register
LogEnrichmentFilter
as a global action filter
Startup.cs
:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<LogEnrichmentFilter>();
services.AddMvc(o =>
{
o.Filters.Add<LogEnrichmentFilter>();
});
You should then have the current username in the log context for code that runs in the MVC action invocation pipeline. I imagine the username would be attached to a few more log entries if you used a resource filter instead of an action filter, as they run slightly earlier in the pipeline (I've only just found out about these!)
来源:https://stackoverflow.com/questions/51261177/add-username-into-serilog