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

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

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:

确定