TGStat
TGStat
Qidiruv uchun matnni kiriting
Ilg‘or kanal qidiruvi
  • flag Uzbek
    Sayt tili
    flag Russian flag English flag Uzbek
  • Saytga kirish
  • Katalog
    Kanal va guruhlar katalogi Hududiy to‘plamlar Tematik to‘plamlar Платные каналы Kanallar qidiruvi
    Kanal/guruh qo‘shish
  • Reytinglar
    Kanallar reytingi Guruhlar reytingi Postlar reytingi
    Brendlar va shaxslar reytingi
  • Analitika
  • Postlarda qidiruv
  • Telegram'ni kuzatish
  • Targ‘ibot
    Yandex Business orqali reklama TGStat Agency orqali kanallarda reklama TGStat.ru saytida reklama
.NET sh blog

29 Apr 2024, 14:26

Telegram'da ochish Ulashish Shikoyat qilish

Теперь рассмотрим область действия этого LoggingScope.

Метод BeginScope возвращает IDisposable который по-умолчанию мы оборачиваем в using.
После того как LoggingScope будет удален, эти данные из словаря перестанут прикрепляться в логи.

Это очень удобно, но есть нюансы из-за особенности обработки исключений, так как при выбросе исключения CLR выполняет размотку стека вызовов (unwind stack), чтобы найти метод с блоком catch который может поймать этот тип исключения и в большинстве случаев к моменту захода в блок catch, dispose скоупа уже будет выполнен.
Об этом поведении можно подробно почитать в статье от Stephen Cleary - A New Pattern for Exception Logging

Чтобы учесть все эти нюансы, я обычно скоупы объявляю в начале UseCase, сразу же после проверки аргументов и непосредственно перед блоком try catch, который оборачивает все тело метода UseCase.
А чтобы при любой ситуации был присвоен код ошибки, в блоке catch ловлю любые не доменные исключения и
превращаю их в доменное исключение со специальным для данного юзкейса кодом ошибки.

Выглядит это примерно так:
public async Task ExecuteAsync(Command command, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(command);

// здесь объявляем скоупы

try
{
// тело сценария
}
catch (Exception e) when (e is not DomainException)
{
throw new DomainException(ErrorCodes.MyUseCaseFailed, innerException: e);
}
}
При этом явно логировать доменные исключения нет необходимости, так как в их конструкторе вызывается функция OnExceptionCreated, который используется в Composition Root для логирования исключений, еще до выброса этого исключения, а значит в том же контексте.

Теперь представьте что абонент пытается вернуть книгу, которой нет в базе данных вообще. Вернуть несуществующую книгу нельзя, поэтому будет возвращен соответствующий код ошибки и добавлен лог об этом:
{
"Timestamp": "2024-04-29T14:03:44.7722095+05:00",
"Level": "Information",
"MessageTemplate": "{ErrorCode}: {Message}",
"TraceId": "054b1bd4b647d40442ef57bf898e913d",
"SpanId": "0399f7806cbd8721",
"Exception": "BL00032: This book not found or not borrowed by abonent\r\nBookLibrary.Domain.Exceptions.BookLibraryException: This book not found or not borrowed by abonent",
"Properties": {
"ErrorCode": "BL00032",
"Message": "This book not found or not borrowed by abonent",
"EventId": { "Id": 1, "Name": "LogDomainException" },
"SourceContext": "Sstv.DomainExceptions.DomainException",
"AbonentId": "018e8ed6-27a3-75bd-b28e-1afd9f5c3bd6",
"BookId": "018e90a5-88e5-7566-be05-a8a6fc60c932",
"ActionId": "26f62c17-4e2a-4b47-a1af-0623c4d36052",
"ActionName": "BookLibrary.Api.Features.ReturnBookController.ReturnBookAsync (BookLibrary.Api)",
"RequestId": "0HN385D0E35F5:00000001",
"RequestPath": "/api/v1/books/return",
"ConnectionId": "0HN385D0E35F5",
"Host": "My-PC",
"ErrorId": "c508483a-aac7-4bb9-97bb-c398346437dd"
}
}
Как мы видим, в Properties есть и BookId и AbonentId, которые я добавляю в скоупы в начале UseCase и есть много других контекстных данных проставленных самим AspNetCore.

Если выбросить не доменное исключение внутри try catch, то размотка стека будет выполнена частично (не успеем выйти за пределы текущего метода т.к. catch это исключение поймает), поэтому объявленные скоупы все ещё будут доступны при логировании.

Получается что при любом раскладе, скоупы в UseCase работают так как от них ожидается.

Если бы UseCase не был обёрнут в такой try catch или если перенести BeginScope внутрь try catch, то LoggingScope будут удалены к моменту входа в catch блок.

Еще настоятельно рекомендую наименования переменных в LoggingScope писать единообразно, в идеале зафиксировав их имена где-нибудь в константах.
Например идентификатор абонента следует везде указывать строго в поле "AbonentId". Создание новых UseCase почти всегда будет с оглядкой на существующие UseCase, поэтому подход будет распространяться сам по себе через копирование вне зависимости делает это джун или сеньор :)

219 0 1 2 5
Katalog
Kanal va guruhlar katalogi Kanallar to‘plamlari Kanallar qidiruvi Kanal/guruh qo‘shish
Reytinglar
Telegram-kanallar reytingi Telegram-guruhlar reytingi Postlar reytingi Brendlar va shaxslar reytingi
API
Statistika API'si Postlar qidiruvi API'si API Callback
Kanallarimiz
@TGStat @TGStat_Chat @telepulse @TGStatAPI
O‘qish
Академия TGStat Telegram tadqiqoti 2019 Telegram tadqiqoti 2021 Telegram tadqiqoti 2023
Kontaktlar
Справочный центр Qo‘llab-quvvatlash Email Vakansiyalar
Har xil narsalar
Foydalanuvchi shartnomasi Maxfiylik siyosati Ommaviy oferta
Botlarimiz
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot