Настройте NServiceBus, чтобы регистрировать исключения, которые вызывают повторные попытки сообщения
Я использую NServiceBus 4.6 и Serilog. Я настроил NServiceBus для использования Serilog через:
global::NServiceBus.SetLoggingLibrary.Custom(new SeriLoggerFactory());
Сама фабрика тоже очень простая:
public class SeriLoggerFactory : ILoggerFactory
{
public ILog GetLogger(Type type)
{
return new SeriLoggerAdapter(Log.ForContext(type));
}
public ILog GetLogger(string name)
{
var contextLogger = Log.ForContext("SourceContext", name);
return new SeriLoggerAdapter(contextLogger);
}
}
Я определенно получаю записи в журнале, связанные с NServiceBus, но одна вещь, которая отсутствует, это детали исключения, когда сообщение обрабатывается, но выдается исключение. Я могу видеть информацию об исключении в заголовках сообщений NServiceBus (либо непосредственно, просматривая сообщение в очереди ошибок, либо через Service Insight), но в сообщении, зарегистрированном NServiceBus, отсутствует наиболее релевантная информация:
Сообщение с идентификатором '0d255d19-85f9-4915-a27c-a41000da12ed' не удалось выполнить FLR и будет передано SLR для повторной попытки 1
или же
SLR не удалось решить проблему с сообщением 0d255d19-85f9-4915-a27c-a41000da12ed и будет перенаправлено в очередь ошибок в MYERRORQUEUE
Отсутствие каких-либо подробностей о корневом исключении затрудняет отладку. Требуется, чтобы разработчик открыл Service Insight или открыл инструмент для просмотра сообщения в самой очереди. Оба являются громоздкими, и оба испытывают недостаток в любой расширяемости.
Например, Serilog позволяет вам создавать классы ILogEventEnricher, которые могут регистрировать специальные сведения об исключении, о котором идет речь, - вещи, которые не регистрируются простым.ToString для исключения. Без NServiceBus, фактически регистрирующего мои исключения, у меня нет никакого способа извлечь эти детали.
Что мне здесь не хватает?
1 ответ
NServiceBus имеет класс с именем NServiceBus.Faults.ErrorsNotifications, который содержит следующие наблюдаемые:
- MessageSentToErrorQueue
- MessageHasFailedAFirstLevelRetryAttempt
- MessageHasBeenSentToSecondLevelRetries
Вы можете подписаться на эти наблюдаемые при запуске конечной точки, как в следующем примере, в котором регистрируются сообщения об ошибках, отправляемые на первый уровень повторных попыток:
public class GlobalErrorHandler : IWantToRunWhenBusStartsAndStops
{
private readonly ILogger _logger;
private readonly BusNotifications _busNotifications;
readonly List<IDisposable> _notificationSubscriptions = new List<IDisposable>();
public GlobalErrorHandler(ILogger logger, BusNotifications busNotifications)
{
_logger = logger;
_busNotifications = busNotifications;
}
public void Start()
{
_notificationSubscriptions.Add(_busNotifications.Errors.MessageHasFailedAFirstLevelRetryAttempt.Subscribe(LogWhenMessageSentToFirstLevelRetry));
}
public void Stop()
{
foreach (var subscription in _notificationSubscriptions)
{
subscription.Dispose();
}
}
private void LogWhenMessageSentToFirstLevelRetry(FirstLevelRetry message)
{
var properties = new
{
MessageType = message.Headers["NServiceBus.EnclosedMessageTypes"],
MessageId = message.Headers["NServiceBus.MessageId"],
OriginatingMachine = message.Headers["NServiceBus.OriginatingMachine"],
OriginatingEndpoint = message.Headers["NServiceBus.OriginatingEndpoint"],
ExceptionType = message.Headers["NServiceBus.ExceptionInfo.ExceptionType"],
ExceptionMessage = message.Headers["NServiceBus.ExceptionInfo.Message"],
ExceptionSource = message.Headers["NServiceBus.ExceptionInfo.Source"],
TimeSent = message.Headers["NServiceBus.TimeSent"]
};
_logger.Error("Message sent to first level retry. " + properties, message.Exception);
}
}
Observable реализуется с помощью Reactive Extensions, поэтому вам придется установить пакет NuGet Rx-Core, чтобы это работало.