c# - Stephen Cleary 关于异步的新日志记录模式问题
问题描述
在我测试过Stephen Cleary 的新日志模式后,我很喜欢它。但是,有一点我不明白:
不幸的是,这里的解决方案不适用于异步代码。这是因为异步会导致异常被捕获,然后在等待时重新抛出。因此,异常过滤器在等待点而不是最初引发异常的位置运行。
我想举一个例子,因为我的代码似乎对我来说很好用Handle(...)
/hisTrue(...)
和 no throw;
。
我SubscribeAsync
故意抛出异常。
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
await SubscribeAsync().ConfigureAwait(false);
}
catch (Exception ex) when (Handle(() => _logger.LogError(ex, "Unexpected error.")))
{
}
}
public static class ExceptionFilterUtility
{
public static bool Handle(Action action)
{
action();
return true;
}
public static bool Propagate(Action action)
{
action();
return false;
}
}
我的堆栈跟踪是:
[2021-03-13 20:16:32 Error] ElonMuskBot.Core.TradeManagers.LiveTradeManager
Unexpected error.
ElonMuskBot.Core.Exceptions.RequestFailedException: Error while subscribing to the Spot candlestick update stream
at ElonMuskBot.Core.Clients.SpotBotClient.SubscribeToCandleUpdatesAsync(String symbol, KlineInterval timeInterval, Action`1 onMessage) in E:\GitHub\elonmuskbot\src\ElonMuskBot.Core\Clients\SpotBotClient.cs:line 88
at ElonMuskBot.Core.TradeManagers.LiveTradeManager.SubscribeAsync() in E:\GitHub\elonmuskbot\src\ElonMuskBot.Core\TradeManagers\LiveTradeManager.cs:line 54
at ElonMuskBot.Core.TradeManagers.LiveTradeManager.StartAsync(CancellationToken cancellationToken) in E:\GitHub\elonmuskbot\src\ElonMuskBot.Core\TradeManagers\LiveTradeManager.cs:line 38
解决方案
我想举一个例子,因为我的代码似乎对我来说很好,使用 Handle(...)/his True(...) 并且没有 throw;。
该模式的重点是在throw
. 无论哪种方式,异常处理本身都可以正常工作,但捕获的范围不同。
由于异常过滤器实际上是在throw
. 因此异常记录在throw
,包括所有存在的日志记录范围。
但是,它不适用于异步代码。异常将被很好地记录,但记录范围将不存在。这是因为异常过滤器在 的点执行await
,而不是在throw
.
因此,这是一个无法正确捕获异常日志记录范围的示例:
public async Task SubscribeAsync()
{
using var _ = _logger.BeginScope("This will not be logged");
await Task.Yield();
throw new InvalidOperationException("This will be logged");
}
自从写了那篇博文以来,我开发了一个更好的解决方案,它同时适用于同步和异步异常,并将它作为一个库发布。我还没来得及写一篇新的博客文章(对不起!),但一旦我这样做了,我就会将旧的解决方案指向新的解决方案。
较新的解决方案仅适用于 .NET Core,因为它与ILogger
. 示例用法:
// Don't forget to call IServiceCollection.AddExceptionLoggingScopes() in your startup.
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
await SubscribeAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
using (_logger.BeginCapturedExceptionLoggingScopes(ex))
_logger.LogError(ex, "Unexpected error.");
}
}
推荐阅读
- php - 如何限制共享主机上的 PHP 代码文件系统操作?
- ruby - 捆绑安装不起作用:Bundler::HTTPError 无法从 rubygems.org 获取规范
- flutter - 如果正文有两个脚手架,如何关闭应用程序页面
- xpath - 下一页的 Xpath - Scrapy
- javascript - React 防止焦点窃取 onClick
- python - 如何在 Heroku 中运行 UVICORN?
- python - 分类问题是否可以使用词嵌入 + 其他特征?
- excel - 通过比较名字、姓氏或中间名是否匹配来比较两个单元格中的名称。使用 vba 代码匹配最多 70%
- c# - 如何从类库中获取嵌入式资源?
- android - 从Android 10中的最新应用程序中杀死应用程序时如何停止绑定服务