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

Asp.net core 使用Serilog和Asp.Net Core时将用户添加到日志上下文,asp.net-core,serilog,Asp.net Core,Serilog,我正在尝试将Serilog与我的ASP.NETCore1.0项目结合使用。我似乎无法将当前登录的用户添加到已登录的属性中 有人知道了吗 我试过这个: using System.Threading.Tasks; using Serilog.Context; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using System.Security.Claims; using xxx.Models; name

我正在尝试将Serilog与我的ASP.NETCore1.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>();
        }
    }
}
但这总是以“匿名”作为用户名结束

提前谢谢


Søren Rokkedal

您需要在如下块中调用
\u 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);
    }
}

我只需要几行代码就可以获得经过身份验证的Active Directory用户。我在核心身份验证方面不是很有经验,尤其是在声明方面,但这可能会让你走上正轨,或者至少会帮助其他人解决与你类似的问题,但与AD有关的问题

关键行是
Enrich.FromLogContext()
app.Use(异步…

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>

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

更新

实际上,即使将此中间件移动到身份验证中间件下方也可能不够。似乎可以在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>();
        });
        ...
    }
public void配置服务(IServiceCollection服务)
{
services.addScope();
services.AddMvc(o=>
{
o、 Filters.Add();
});
...
}

LoggerConfiguration
对象上使用
丰富.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();
公共静态IWebHost BuildWebHost(字符串[]args)=>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext,config)=>
{
config.ClearProviders();
})
.UseStartup()
.useserlog((ctx,cfg)=>
{
cfg.ReadFrom.Configuration(ctx.Configuration)
.Enrich.WithEnvironmentUserName()
})
.Build();
我在web.config中也有以下设置,尽管我还没有证明所有IIS服务器上是否都需要:


您尝试过在身份验证后将其添加到管道中吗?不确定这是什么意思我的意思是调用
app.enrichloger();
调用
app.UseAuthentication(..);
,或者不管您的身份验证方法是什么。您在对用户进行身份验证之前过早地在管道中注入记录器,因此它将始终是匿名的。我有app.UseIdentity()在我呼吁丰富logger@SørenRokkedal..感谢这是一篇老帖子,但你有没有发现这一点?谢谢你的回复。我相信最初的帖子包含了wait_next(httpContext)。实现建议的解决方案无法使其正常工作。每次调用Invoke方法时,httpContext.User.Identity.IsAuthenticated都是错误的。有什么想法吗?感谢您的后续行动。那么对此不确定。您有
Enrich.FromLogContext()吗
设置您的
日志配置
?是的,我已将其配置为所示的名称,
WithEnvironmentUserName
将从环境变量获取用户-因此它将获取服务器的用户名,而不是当前web app用户的用户名。您对中间件方法提出了一些有效的观点,但用户名不是这种方法在任何地方都可以使用-例如,它不是为
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Mvc.ViewFeatures
Microsoft.AspNetCore.Routing.EndpointMiddleware
和许多其他方法记录的
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();