首页 文章

使用Serilog和Asp.Net Core时,将用户添加到日志上下文中

提问于
浏览
12

我正在尝试将Serilog与我的ASP.Net Core 1.0项目一起使用 . 我似乎无法将当前登录的用户添加到已记录的属性中 .

有人想出来了吗?

我试过这个:

using System.Threading.Tasks;
using Serilog.Context;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using xxx.Models;

namespace xxx.Utils
{
    public class EnrichSerilogContextMiddleware
    {
        private readonly RequestDelegate _next;
        public EnrichSerilogContextMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {

            var username = httpContext.User.Identity.Name;
            if (httpContext.User.Identity.IsAuthenticated)
            {
                var userFullName = (((ClaimsIdentity)httpContext.User.Identity).FindFirst(Member.FullnameClaimName).Value);
                var userName = "anyone@gmail.com";
                LoggerEnricher.AddEntryPointContext(userFullName, userName);
            }
            else
            {
                LoggerEnricher.AddEntryPointContext();
            }


            await _next(httpContext);
        }
    }

    public static class LoggerEnricher

    {
        public static void AddEntryPointContext(string userFullName = null, string username = null)
        {
            if (!string.IsNullOrWhiteSpace(username) || !string.IsNullOrWhiteSpace(userFullName))
            {
                LogContext.PushProperty("Username", username);
                LogContext.PushProperty("UserFullename", userFullName);
            }
            else
            {
                LogContext.PushProperty("Username", "Anonymous");
            }

        }

        public static void EnrichLogger(this IApplicationBuilder app)
        {
            app.UseMiddleware<EnrichSerilogContextMiddleware>();
        }
    }
}

我通过添加以下命令在Startup.cs中触发:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
        loggerFactory.AddSerilog();
        app.EnrichLogger();
        ...
    }

但这最终会以“匿名”作为用户名 .

提前致谢

SørenRokkedal

4 回答

  • -2

    LoggerConfiguration 对象上使用 .Enrich.WithEnvironmentUserName() 方法,例如:

    public static IWebHost BuildWebHost(string[] args) =>
      WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging((hostingContext, config) =>
        {
          config.ClearProviders();
        })
        .UseStartup<Startup>()             
        .UseSerilog((ctx, cfg) =>
        {
          cfg.ReadFrom.Configuration(ctx.Configuration)
            .Enrich.WithEnvironmentUserName()
        })
        .Build();
    

    我在web.config中也有以下设置,虽然我没有证明是否在所有IIS服务器上都需要它:

    <system.webServer><aspNetCore ... forwardWindowsAuthToken="true"> ...

    取自这里:Logging in ASP NET Core with Serilog

  • 1

    您需要在块中调用 _next ,如下所示:

    public async Task Invoke(HttpContext httpContext)
    {
        if (httpContext.User.Identity.IsAuthenticated)
        {
            var userFullName = (((ClaimsIdentity)httpContext.User.Identity).FindFirst(Member.FullnameClaimName).Value);
            var userName = "anyone@gmail.com";
    
            using (LogContext.PushProperty("Username", userName))
            using (LogContext.PushProperty("UserFullName", userFullName))
            {
                await _next(httpContext);
            }
        }
        else
        {
            await _next(httpContext);
        }
    }
    
  • 3

    我只需几行代码即可获得经过身份验证的Active Directory用户 . 我对Core身份验证没有太多经验,尤其是声称,但是这可能会让你在途中或者至少帮助那些带有类似问题的人,但使用AD .

    关键线是 Enrich.FromLogContext()app.Use(async...

    public class Startup
    {
        public IConfigurationRoot Configuration { get; }
    
        public Startup(IHostingEnvironment env)
        {
            Log.Logger = new LoggerConfiguration()
                       .Enrich.FromLogContext() // Populates a 'User' property on every log entry
                       .WriteTo.MSSqlServer(Configuration.GetConnectionString("MyDatabase"), "Logs")
                       .CreateLogger();
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.WithFilter(new FilterLoggerSettings
                                     {
                                         { "Default", LogLevel.Information },
                                         { "Microsoft", LogLevel.Warning },
                                         { "System", LogLevel.Warning }
                                     })
                         .AddSerilog();
    
            app.Use(async (httpContext, next) =>
                    {
                        var userName = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "unknown";
                        LogContext.PushProperty("User", !String.IsNullOrWhiteSpace(userName) ? userName : "unknown");
                        await next.Invoke();
                    });
        }
    }
    

    对于通过IIS / Kestrel进行AD身份验证,web.config需要 forwardWindowsAuthToken 设置,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.webServer>
        <aspNetCore ... forwardWindowsAuthToken="true" />
      </system.webServer>
    </configuration>
    
  • 5

    你的中间件可能很好 . 但是配置中间件的顺序很重要 . 您的EnrichLogger中间件是第一个 . 这意味着它在身份验证中间件之前运行 . 将 app.EnrichLogger 调用移到您添加身份验证中间件(可能是 app.UseAuthentication )的下方 . 这样,当您的EnrichLogger中间件运行时,将正确设置HttpContext.User属性 .

    Update

    实际上,即使将这个中间件移到认证中间件下面也可能还不够 . 似乎可以在MVC中间件内设置身份(至少在某些配置中) . 这意味着在执行控制器操作之后(通过在MVC中间件之后将其移动),您无法从中间件访问用户标识 . 但是在日志中使用它已经太晚了 .

    相反,您可能必须使用MVC筛选器将用户信息添加到日志上下文中 . 例如,您可以创建一个这样的过滤器:

    public class LogEnrichmentFilter : IActionFilter
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            var httpContext = _httpContextAccessor.HttpContext;
            if (httpContext.User.Identity.IsAuthenticated)
            {
                LogContext.PushProperty("Username", httpContext.User.Identity.Name);
            }
            else
            {
                LogContext.PushProperty("Username", "Anonymous");
            }
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }
    }
    

    然后,您可以使用DI全局应用过滤器 . 在您的Services.cs文件中:

    public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<LogEnrichmentFilter>();
            services.AddMvc(o =>
            {
                o.Filters.Add<LogEnrichmentFilter>();
            });
            ...
        }
    

相关问题