在Blazor(服务器)中可以使用ILogger.BeginScope()吗?

huangapple go评论85阅读模式
英文:

Can I use ILogger.BeginScope() in Blazor (server)?

问题

I understand that you want a translation of the code-related portions. Here are the translated code snippets:

// Original Code:
string username = user.Name;
using (Logger.BeginScope("User:{username}", username))
{
    if (Logger.IsEnabled(LogLevel.Trace))
        Logger.LogTrace("/Manager/Campaign ModeState={ModeState} UniqueId={UniqueId}");
}

// Translated Code:
string username = user.Name;
使用(Logger.BeginScope("用户:{username}", username))
{
    if (Logger.IsEnabled(LogLevel.Trace))
        Logger.LogTrace("/Manager/Campaign ModeState={ModeState} UniqueId={UniqueId}");
}

// Original Code:
public class UserScopeMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<UserScopeMiddleware> _logger;

    public UserScopeMiddleware(RequestDelegate next, ILogger<UserScopeMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.User.Identity is { IsAuthenticated: true })
        {
            var username = context.User.Identity.Name;

            using (_logger.BeginScope("User:{username}", username))
            {
                await _next(context);
            }
        }
        else
        {
            await _next(context);
        }
    }
}

// Translated Code:
public class UserScopeMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<UserScopeMiddleware> _logger;

    public UserScopeMiddleware(RequestDelegate next, ILogger<UserScopeMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.User.Identity is { IsAuthenticated: true })
        {
            var username = context.User.Identity.Name;

            using (_logger.BeginScope("用户:{username}", username))
            {
                await _next(context);
            }
        }
        else
        {
            await _next(context);
        }
    }
}

// Original Code:
builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole();

// Translated Code:
builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole();

I hope these translations help you understand the code better.

英文:

I have the following code for a page in my code:

string username = user.Name;
using (Logger.BeginScope(&quot;User:{username}&quot;, username))
{
if (Logger.IsEnabled(LogLevel.Trace))
Logger.LogTrace($&quot;/Manager/Campaign ModeState={ModeState} UniqueId={UniqueId}&quot;);
}

And when this is logged (yes set to Trace) I get this:

&#39;LouisHowe.web.exe&#39; (CoreCLR: clrhost): Loaded &#39;C:\git\LouisHowe\LouisHowe.web\bin\Debug\net7.0\Newtonsoft.Json.dll&#39;. Skipped loading symbols. Module is optimized and the debugger option &#39;Just My Code&#39; is enabled.
LouisHowe.web.Pages.Manager.CampaignPage: Trace: /Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder
Microsoft.EntityFrameworkCore.Query: Warning: Compiling a query which loads related collections for more than one collection navigation, either via &#39;Include&#39; or through projection, but no &#39;QuerySplittingBehavior&#39; has been configured. By default, Entity Framework will use &#39;QuerySplittingBehavior.SingleQuery&#39;, which can potentially result in slow query performance. See https://go.microsoft.com/fwlink/?linkid=2134277 for more information. To identify the query that&#39;s triggering this warning call &#39;ConfigureWarnings(w =&gt; w.Throw(RelationalEventId.MultipleCollectionIncludeWarning))&#39;.

So no "User:david@whatever.com"

Is BeginScope ignored in Blazor server? Do I need to set something else?

And if it can be done, I also have the following (to add this to every log) - also does not work

Program.cs:

app.UseAuthentication();
app.UseAuthorization();
// must be after UseAuthentication/UseAuthorization
app.UseMiddleware&lt;UserScopeMiddleware&gt;();

UserScopeMiddleware.cs:

public class UserScopeMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger&lt;UserScopeMiddleware&gt; _logger;
public UserScopeMiddleware(RequestDelegate next, ILogger&lt;UserScopeMiddleware&gt; logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.User.Identity is { IsAuthenticated: true })
{
var username = context.User.Identity.Name;
using (_logger.BeginScope(&quot;User:{username}&quot;, username))
{
await _next(context);
}
}
else
{
await _next(context);
}
}
}

Anything I need to add to make this work?


I changed my logging setup to:

builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole();

and now in the console I get:

  &quot;Timestamp&quot;: &quot;23:18:00 &quot;,
&quot;EventId&quot;: 0,
&quot;LogLevel&quot;: &quot;Warning&quot;,
&quot;Category&quot;: &quot;LouisHowe.web.Pages.Manager.CampaignPage&quot;,
&quot;Message&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;,
&quot;State&quot;: {
&quot;Message&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;,
&quot;{OriginalFormat}&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;
},
&quot;Scopes&quot;: [
{
&quot;Message&quot;: &quot;SpanId:2cc5a3dbc3bfb348, TraceId:230638cecbe62fa814c1cca7a29c4966, ParentId:0000000000000000&quot;,
&quot;SpanId&quot;: &quot;2cc5a3dbc3bfb348&quot;,
&quot;TraceId&quot;: &quot;230638cecbe62fa814c1cca7a29c4966&quot;,
&quot;ParentId&quot;: &quot;0000000000000000&quot;
},
{
&quot;Message&quot;: &quot;ConnectionId:0HMRO5LSIICLH&quot;,
&quot;ConnectionId&quot;: &quot;0HMRO5LSIICLH&quot;
},
{
&quot;Message&quot;: &quot;RequestPath:/_blazor RequestId:0HMRO5LSIICLH:00000025&quot;,
&quot;RequestId&quot;: &quot;0HMRO5LSIICLH:00000025&quot;,
&quot;RequestPath&quot;: &quot;/_blazor&quot;
},
{
&quot;Message&quot;: &quot;TransportConnectionId:P5EJP_vUZlsTIYRpRDkuAQ&quot;,
&quot;TransportConnectionId&quot;: &quot;P5EJP_vUZlsTIYRpRDkuAQ&quot;
},
{
&quot;Message&quot;: &quot;User:colorado@thielen.com&quot;,
&quot;username&quot;: &quot;colorado@thielen.com&quot;,
&quot;{OriginalFormat}&quot;: &quot;User:{username}&quot;
}
]

so Logger.BeginScope works!

However, if I remove the

using (Logger.BeginScope(&quot;User:{username}&quot;, username))
{
}

so that I'm dependent on the middleware - that does not work. With the explicit BeginScope gone I get:

{
&quot;Timestamp&quot;: &quot;23:18:00 &quot;,
&quot;EventId&quot;: 0,
&quot;LogLevel&quot;: &quot;Warning&quot;,
&quot;Category&quot;: &quot;LouisHowe.web.Pages.Manager.CampaignPage&quot;,
&quot;Message&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;,
&quot;State&quot;: {
&quot;Message&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;,
&quot;{OriginalFormat}&quot;: &quot;/Manager/Campaign ModeState=Read UniqueId=Colorado_campaign_Boulder&quot;
},
&quot;Scopes&quot;: [
{
&quot;Message&quot;: &quot;SpanId:adb3b8b31b1b0cf6, TraceId:af46f44789291db3fef2c8e45229dcb0, ParentId:0000000000000000&quot;,
&quot;SpanId&quot;: &quot;adb3b8b31b1b0cf6&quot;,
&quot;TraceId&quot;: &quot;af46f44789291db3fef2c8e45229dcb0&quot;,
&quot;ParentId&quot;: &quot;0000000000000000&quot;
},
{
&quot;Message&quot;: &quot;ConnectionId:0HMRO5NIOFFT0&quot;,
&quot;ConnectionId&quot;: &quot;0HMRO5NIOFFT0&quot;
},
{
&quot;Message&quot;: &quot;RequestPath:/_blazor RequestId:0HMRO5NIOFFT0:00000025&quot;,
&quot;RequestId&quot;: &quot;0HMRO5NIOFFT0:00000025&quot;,
&quot;RequestPath&quot;: &quot;/_blazor&quot;
},
{
&quot;Message&quot;: &quot;TransportConnectionId:7HQYBvsIgykJEIJzGgdjBg&quot;,
&quot;TransportConnectionId&quot;: &quot;7HQYBvsIgykJEIJzGgdjBg&quot;
}
]

so... anyway to get the middleware working?

答案1

得分: 0

好的,以下是翻译好的代码部分:

好的,关键是创建一个ILogger包装器。因此,创建了这个类:

/// <summary>
/// 提供一个记录器,其中每个日志都将具有包含用户的AspNetId和电子邮件的范围。
/// </summary>
public class ScopedLoggerFactory
{

    private readonly ILoggerFactory _factory;
    private readonly AuthenticationStateProvider _provider;

    public ScopedLoggerFactory(ILoggerFactory factory, AuthenticationStateProvider provider)
    {
        _factory = factory;
        _provider = provider;
    }

    /// <summary>
    /// 获取ILogger。
    /// </summary>
    /// <typeparam name="T">记录器将记录的类。</typeparam>
    /// <returns>具有每个Log调用上设置的范围的ILogger。</returns>
    public async Task<ScopedLogger<T>> GetLogger<T>()
    {
        var logger = _factory.CreateLogger<T>();

        var user = await _provider.GetAuthenticationStateAsync();
        var username = user.User.Identity?.Name;
        string? aspNetId = user.User.Claims.FirstOrDefault(c =>
                    c.Type ==
                    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value;

        return new ScopedLogger<T>(logger, aspNetId, username);
    }
}

/// <summary>
/// 一个ILogger,其中每次调用Log()都将在包含用户信息的范围内。
/// </summary>
/// <typeparam name="T">记录器将记录的类。</typeparam>
public class ScopedLogger<T> : ILogger<T>
{

    private readonly ILogger<T> _logger;
    private readonly string? aspNetId;
    private readonly string? username;

    public ScopedLogger(ILogger<T> logger, string? aspNetId, string? username)
    {
        _logger = logger;
        this.aspNetId = aspNetId;
        this.username = username;
    }

    /// <inheritdoc />
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
        Func<TState, Exception?, string> formatter)
    {
        using (_logger.BeginScope("User:{username}, {aspNetId}", username, aspNetId))
            _logger.Log(logLevel, eventId, state, exception, formatter);
    }

    /// <inheritdoc />
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        return _logger.BeginScope(state);
    }

    /// <inheritdoc />
    public bool IsEnabled(LogLevel logLevel)
    {
        return _logger.IsEnabled(logLevel);
    }
}

然后在whatever.razor.cs文件中:

[Inject]
private ScopedLoggerFactory ScopedLoggerFactory { get; set; } = default!;

ScopedLogger<CampaignPage> Logger { get; set; } = default!;

// ...

Logger = await ScopedLoggerFactory.GetLogger<CampaignPage>();

希望这对你有所帮助。如果有任何其他问题,请随时提出。

英文:

Ok, the trick is to create an ILogger wrapper. So created this class:

/// &lt;summary&gt;
/// Provides a logger where each log will have a scope with the User&#39;s AspNetId and email.
/// &lt;/summary&gt;
public class ScopedLoggerFactory
{
private readonly ILoggerFactory _factory;
private readonly AuthenticationStateProvider _provider;
public ScopedLoggerFactory(ILoggerFactory factory, AuthenticationStateProvider provider)
{
_factory = factory;
_provider = provider;
}
/// &lt;summary&gt;
/// Get the ILogger.
/// &lt;/summary&gt;
/// &lt;typeparam name=&quot;T&quot;&gt;The class the logger is going to log in.&lt;/typeparam&gt;
/// &lt;returns&gt;AN ILogger with the scope set on each Log call.&lt;/returns&gt;
public async Task&lt;ScopedLogger&lt;T&gt;&gt; GetLogger&lt;T&gt;()
{
var logger = _factory.CreateLogger&lt;T&gt;();
var user = await _provider.GetAuthenticationStateAsync();
var username = user.User.Identity?.Name;
string? aspNetId = user.User.Claims.FirstOrDefault(c =&gt; c.Type ==
&quot;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier&quot;)?.Value;
return new ScopedLogger&lt;T&gt;(logger, aspNetId, username);
}
}
/// &lt;summary&gt;
/// An ILogger where each Log() call will be in a scope with the user information.
/// &lt;/summary&gt;
/// &lt;typeparam name=&quot;T&quot;&gt;The class the logger is going to log in.&lt;/typeparam&gt;
public class ScopedLogger&lt;T&gt; : ILogger&lt;T&gt;
{
private readonly ILogger&lt;T&gt; _logger;
private readonly string? aspNetId;
private readonly string? username;
public ScopedLogger(ILogger&lt;T&gt; logger, string? aspNetId, string? username)
{
_logger = logger;
this.aspNetId = aspNetId;
this.username = username;
}
/// &lt;inheritdoc /&gt;
public void Log&lt;TState&gt;(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func&lt;TState, Exception?, string&gt; formatter)
{
using (_logger.BeginScope(&quot;User:{username}, {aspNetId}&quot;, username, aspNetId))
_logger.Log(logLevel, eventId, state, exception, formatter);
}
/// &lt;inheritdoc /&gt;
public IDisposable? BeginScope&lt;TState&gt;(TState state) where TState : notnull
{
return _logger.BeginScope(state);
}
/// &lt;inheritdoc /&gt;
public bool IsEnabled(LogLevel logLevel)
{
return _logger.IsEnabled(logLevel);
}
}

And then in the whatever.razor.cs files:

[Inject]
private ScopedLoggerFactory ScopedLoggerFactory { get; set; } = default!;
ScopedLogger&lt;CampaignPage&gt; Logger { get; set; } = default!;
// ...
Logger = await ScopedLoggerFactory.GetLogger&lt;CampaignPage&gt;();

huangapple
  • 本文由 发表于 2023年6月29日 05:57:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定