ASP.NET Core中的日志配置更改 [英] Log configuration changes in ASP.NET Core
问题描述
我要在更改配置时记录日志.
I want to log when configuration is changed.
我在 code> Program.cs 或 Startup.cs
:
I do this in Program.cs
or Startup.cs
:
ChangeToken.OnChange(
() => configuration.GetReloadToken(),
state => logger.Information("Configuration reloaded"),
(object)null
);
但是我收到了两次变更报告,因此需要将其删除.建议是这样做:
But I get double change reports, so it needs to be debounced. The advice is to do this:
ChangeToken.OnChange(
() => configuration.GetReloadToken(),
state => { Thread.Sleep(2000); logger.Information("Configuration reloaded"); },
(object)null
);
我在这里使用 2000
,因为我不确定什么是合理的值.
I'm using 2000
here as I'm not sure what's a reasonable value.
我发现有时候我仍然可以检测到多个变更,相隔2000毫秒.因此,反跳对我不起作用,只会导致报告的更改之间出现延迟.如果我设置一个很高的值,那么我只会得到一份报告,但这并不理想(并且掩盖了问题).
I've found that sometimes I still get multiple change detections, separated by 2000 milliseconds. So the debounce doesn't work for me, just causes a delay between reported changes. If I set a high value then I only get one report, but that isn't ideal (and conceals the problem).
所以我想知道:
- 这真的是在反跳,还是只是对已报告的更改进行排队?
- 我使用1000到5000的值来获得不同的成功.别人在用什么?
- 睡眠是否已发布到服务器的主线程?我希望不会!
推荐答案
讨论了多项更改检测问题此处(以及多个存储库中的至少十二个其他问题)是他们拒绝使用内置机制解决的问题.
The multiple change detection issue discussed here (and at least a dozen other issues in multiple repos) is something they refuse to address using a built-in mechanism.
MS文档使用文件散列方法,但我认为反跳效果更好.
The MS docs use a file hashing approach, but I think that debouncing is better.
我的解决方案使用异步(避免异步同步,这可能会意外炸毁某些东西)和
My solution uses async (avoids async-in-sync which could blow up something accidentally) and a hosted service that debounces change detections.
Debouncer.cs
:
public sealed class Debouncer : IDisposable {
public Debouncer(TimeSpan? delay) => _delay = delay ?? TimeSpan.FromSeconds(2);
private readonly TimeSpan _delay;
private CancellationTokenSource? previousCancellationToken = null;
public async Task Debounce(Action action) {
_ = action ?? throw new ArgumentNullException(nameof(action));
Cancel();
previousCancellationToken = new CancellationTokenSource();
try {
await Task.Delay(_delay, previousCancellationToken.Token);
await Task.Run(action, previousCancellationToken.Token);
}
catch (TaskCanceledException) { } // can swallow exception as nothing more to do if task cancelled
}
public void Cancel() {
if (previousCancellationToken != null) {
previousCancellationToken.Cancel();
previousCancellationToken.Dispose();
}
}
public void Dispose() => Cancel();
}
ConfigWatcher.cs
:
public sealed class ConfigWatcher : IHostedService, IDisposable {
public ConfigWatcher(IServiceScopeFactory scopeFactory, ILogger<ConfigWatcher> logger) {
_scopeFactory = scopeFactory;
_logger = logger;
}
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<ConfigWatcher> _logger;
private readonly Debouncer _debouncer = new(TimeSpan.FromSeconds(2));
private void OnConfigurationReloaded() {
_logger.LogInformation("Configuration reloaded");
// ... can do more stuff here, e.g. validate config
}
public Task StartAsync(CancellationToken cancellationToken) {
ChangeToken.OnChange(
() => { // resolve config from scope rather than ctor injection, in case it changes (this hosted service is a singleton)
using var scope = _scopeFactory.CreateScope();
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
return configuration.GetReloadToken();
},
async () => await _debouncer.Debounce(OnConfigurationReloaded)
);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public void Dispose() => _debouncer.Dispose();
}
Startup.cs
:
services.AddHostedService<ConfigWatcher>(); // registered as singleton
这篇关于ASP.NET Core中的日志配置更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!