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

时间:2016-10-06 12:48:58

标签: asp.net-core serilog

我正在尝试将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 个答案:

答案 0 :(得分:6)

我只需几行代码即可获得经过身份验证的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>

答案 1 :(得分:3)

您需要在块中调用_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);
    }
}

答案 2 :(得分:2)

你的中间件可能很好。但是配置中间件的顺序很重要。您的EnrichLogger中间件是第一个。这意味着它在认证中间件之前运行。将app.EnrichLogger调用移到您添加身份验证中间件(可能是app.UseAuthentication)的下方。这样,当您的EnrichLogger中间件运行时,将正确设置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>();
        });
        ...
    }

答案 3 :(得分:-3)

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

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