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

28 Aug 2025, 10:03

Telegram'da ochish Ulashish Shikoyat qilish

Как отслеживать все исключения которые возникают в .NET приложении?

Встроенных инструментов 2:

1. С помощью dotnet-counters можно подключиться к работающему .NET процессу и снимать данные с него
2. Метрики приложения (OpenTelemetry / AppMetrics / и т.п.)

Чтобы начать собирать счетчик исключений с OpenTelemetry достаточно вызвать AddRuntimeInstrumentation.
Помимо кол-ва исключений там будет много и других полезных метрик (полный список документируется здесь)

До .NET 9.0 метрика (dotnet_exceptions_total) собирает просто кол-во исключений, а начиная с .NET 9.0 можно смотреть в разрезе по типу исключения, что дает гораздо больше информации о происходящем, но всё еще недостаточно.

Данная метрика собирается по событию AppDomain.Current.FirstChanceException, которая срабатывает на каждый throw любого исключения в текущем процессе.
Поэтому обработчик события должен быть очень быстрым чтобы не тормозить приложение и при этом не выбрасывать новые исключения иначе можно уйти в бесконечный цикл или даже крашнуть рантайм. Полезная ремарка есть в документации события.

Еще важно помнить, что на текущий момент async await разворачивается компилятором в FSM (покрайне мере до async2), внутри которого в случае исключений будут их rethrow на каждый await, а значит чем глубже стек вызовов, тем больше лишних триггеров FirstChanceException. Поэтому метрика с одной стороны врёт, с другой нет. Тут смотря как посмотреть.

А так как на одно исключение может быть создано много событий, то нужно сэмплировать иначе будет очень много бесполезных логов.

Полный листинг доступен на github.
Ниже разберем некоторые моменты:
[ThreadStatic]
private static Dictionary? _sampler;

private void HandleFirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
{
var exceptionType = e.Exception.GetType();

ref var counter = ref CollectionsMarshal.GetValueRefOrAddDefault(_sampler, exceptionType, out var exists);

var needLog = !exists || Interlocked.Increment(ref counter) % LOG_EVERY_N_EXCEPTIONS == 0;

if (needLog)
{
LogExceptionSample(e.Exception, exceptionType.FullName);
}
}
Так как необходимо сэмплировать по каждому типу исключения отдельно, то нужно хранить эти счетчики и здесь подходит словарь (_sampler).
И чтобы атомарно увеличивать счетчики воспользуемся Interlocked. Чтобы с ней работать требуется передать ссылку на значимый тип. Если бы счетчик был просто полем или локальной переменной - то всё было бы просто.
Но так как значение находится в словаре, то воспользуемся CollectionsMarshal.GetValueRefOrAddDefault. Он возьмет ссылку на значение в словаре (если его не было добавит) и дальше передаем в Interlocked.Increment

В лог запишется первое появление исключения и каждые последующие кратные N.

Чтобы приложение не ушло в бесконечный цикл, нужно чтобы метод HandleFirstChanceException вообще не выбрасывал никаких исключений (даже неконтролируемые OOM). Это не всегда возможно обеспечить, особенно если вызывать сторонние библиотеки.

Для этого воспользуемся хаком, который я подглядел в исходниках Microsoft.
Её суть в оборачивании флагом небезопасного участка кода. И пока флаг поднят, порожденные новые исключения будут проигнорированны данным обработчиком в текущем потоке (т.к. ThreadStatic)
[ThreadStatic]
private static bool _handlingFirstChanceException;

private void HandleFirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
{
if (_handlingFirstChanceException)
{
return;
}

_handlingFirstChanceException = true;

try
{
// unsafe code
}
catch (Exception _)
{
// no-op
}
finally
{
_handlingFirstChanceException = false;
}
}
Для любителей high performance кода может быть интересно заглянуть в метод Trim.

В итоге данный ExceptionTracker позволяет залогировать каждые N исключений каждого типа и тем самым иметь больше полезной информации для разбора причин, почему эти исключения возникают.

580 0 8 16
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