Azure Monitor и логи

Разбираемся, как из тумана «у нас что-то тормозит» получить точный ответ: что упало, когда началось и из-за чего именно.

Наблюдаемость (observability) — свойство системы, при котором по её внешним сигналам (метрики, логи, трассировки) можно понять, что происходит внутри, не залезая в неё отладчиком.

Зачем это нужно на практике

Приложение задеплоено, пайплайн зелёный, все довольны. В понедельник приходит менеджер: «Клиенты жалуются, что корзина не открывается». Вы заходите в приложение — работает. Смотрите на App Service — «Running». И всё, дальше идти некуда.

Без телеметрии у вас есть только два состояния: «вроде работает» и «всё упало». Между ними — огромная серая зона, где живут настоящие проблемы: пять процентов запросов отваливаются по таймауту, база отвечает за восемь секунд вместо ста миллисекунд, память течёт и приложение перезапускается раз в час.

Azure Monitor — общий зонтик над всей телеметрией Azure. Внутри него два принципиально разных мира: метрики и логи.

Метрики против логов

Метрики (Metrics)Логи (Logs)
Что эточисла во времени: CPU %, число запросов, время откликасобытия со структурой: запрос, исключение, трасса, обращение к БД
Отвечают на вопрос«что-то не так?»«что именно и почему?»
Скоростьпочти мгновенно, дёшевозадержка десятки секунд, дороже
Хранение93 дня, включено в ценув Log Analytics workspace, платится за объём
Язык запросовKQL (Kusto Query Language)

Правильный рабочий цикл выглядит так: метрика звонит в колокол («ошибок стало в 20 раз больше»), а лог отвечает на вопрос «почему». Пытаться жить на одних метриках — значит знать, что горит, но не знать где. Жить на одних логах — платить за хранение и всё равно узнавать о падении от пользователей.

Application Insights: телеметрия самого приложения

Метрики платформы (CPU, память, HTTP 5xx) Azure собирает сам. Но они ничего не знают про ваше приложение: какой эндпоинт медленный, какой SQL-запрос тормозит, где вылетело исключение. Это работа Application Insights — части Azure Monitor, которая живёт внутри приложения.

Для App Service его чаще всего включают вообще без правки кода — авто-инструментацией:

az monitor app-insights component create \
  --app ai-shop-prod --location westeurope \
  --resource-group rg-shop-prod --workspace law-shop-prod

# Привязываем к веб-приложению (дальше SDK подцепится сам)
az webapp config appsettings set \
  --name shop-prod-web --resource-group rg-shop-prod \
  --settings APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEndpoint=..."

После этого приложение начинает само отправлять телеметрию, которая раскладывается по таблицам:

  • requests — каждый входящий HTTP-запрос: URL, длительность, код ответа, успех/неуспех;
  • dependencies — каждый исходящий вызов: SQL, HTTP к чужому API, обращение к очереди;
  • exceptions — исключения со стектрейсом;
  • traces — ваши собственные логи (logger.LogInformation и т. п.);
  • customEvents — бизнес-события, которые вы отправляете сами («оформлен заказ»).

KQL: язык, на котором задают вопросы логам

Запросы в KQL читаются сверху вниз, данные текут через конвейер |. Похоже на SQL, только человечнее. Смотрим, какие коды ответа отдавало приложение за последний час:

requests
| where timestamp > ago(1h)
| summarize count() by resultCode
| order by count_ desc

Результат:

resultCode   count_
200          14 302
500             418
404              77
302              41

418 пятисоток — это не «вроде работает». Идём глубже: какие именно эндпоинты падают и насколько медленно они отвечают?

requests
| where timestamp > ago(1h) and success == false
| summarize
    failed = count(),
    p95 = percentile(duration, 95)
  by name
| order by failed desc
| take 5

Допустим, лидирует POST /api/cart. Смотрим, что происходило внутри — какие исключения вылетали:

exceptions
| where timestamp > ago(1h)
| summarize count() by type, outerMessage
| order by count_ desc

А теперь главный трюк — распределённая трассировка. У каждого запроса есть operation_Id, и все события, порождённые им (обращения к базе, вызовы соседних сервисов, исключения, ваши логи), несут тот же идентификатор. Значит, по одному упавшему запросу можно поднять всю его историю:

let opId = "a7f3c2e1b8d94a6e9f0c1d2e3f4a5b6c";
union requests, dependencies, exceptions, traces
| where operation_Id == opId
| project timestamp, itemType, name, target, duration, resultCode, outerMessage
| order by timestamp asc

Результат:

timestamp             itemType      name             target        duration  resultCode
10:14:02.113          request       POST /api/cart                 30 041    500
10:14:02.140          dependency    SELECT Carts     sql-shop-prod 29 998    Timeout
10:14:32.121          exception     SqlException                             (Timeout expired)

Вот и весь ответ, за три секунды: приложение не «сломалось» — оно 30 секунд ждало базу, не дождалось и отдало 500. Чинить надо не код корзины, а базу (индекс, блокировка, размер пула соединений). Без трассировки эту гипотезу вы бы проверяли полдня.

Алерты: чтобы узнавать раньше пользователей

Алерт состоит из трёх частей: условие (что считаем проблемой), частота проверки и action group — список получателей и действий: письмо, SMS, webhook в Teams или Slack, запуск Azure Function.

az monitor action-group create \
  --name ag-oncall --resource-group rg-shop-prod \
  --short-name oncall \
  --action email devops devops@shop.example

az monitor metrics alert create \
  --name "5xx выше нормы" \
  --resource-group rg-shop-prod \
  --scopes "/subscriptions/SUB_ID/resourceGroups/rg-shop-prod/providers/Microsoft.Web/sites/shop-prod-web" \
  --condition "total Http5xx > 10" \
  --window-size 5m --evaluation-frequency 1m \
  --severity 2 \
  --action ag-oncall

Метрические алерты дёшевы, быстры и отлично ловят «крупное»: всплеск 5xx, CPU под сотню, отвал экземпляров. Есть и динамические пороги — Azure сам учит норму по истории, что удобно для трафика с суточной сезонностью: в три часа ночи 100 запросов в минуту — это норма, а в полдень — авария.

Отдельно стоит завести availability test: Application Insights раз в несколько минут дёргает ваш URL из разных регионов мира. Это единственный способ узнать, что приложение недоступно снаружи — например, из-за протухшего TLS-сертификата или сломанного DNS, когда изнутри всё «работает».

Как это работает

SDK Application Insights не шлёт каждое событие сразу — он копит их в буфере и отправляет пачками в конечную точку приёма. Оттуда телеметрия попадает в Log Analytics workspace — по сути огромную колоночную базу, оптимизированную под KQL. Задержка между событием и его появлением в запросе обычно от нескольких секунд до пары минут; это нормально и означает, что во время инцидента данные «догоняют» — не спешите делать выводы по последней минуте.

Чтобы объём не взорвался, SDK применяет адаптивную выборку (sampling): при высокой нагрузке он отправляет не все запросы, а часть, и проставляет им коэффициент itemCount. Портал автоматически домножает на него агрегаты, поэтому графики остаются верными. А вот при поиске одного конкретного запроса его может просто не оказаться в данных — и это не баг. Важно: выборка умная и сохраняет связанные события целиком, чтобы трассировка не разваливалась.

Про деньги — здесь их теряют чаще всего. Приём данных бесплатен в пределах примерно 5 ГБ в месяц на workspace, дальше — порядка нескольких долларов за гигабайт. Метрики платформы бесплатны. Хранение включено на 30–90 дней, сверх того — платно. Звучит безобидно, пока кто-нибудь не включит на проде уровень логирования Debug: приложение начинает писать десятки гигабайт в сутки, и счёт за мониторинг легко обгоняет счёт за само приложение.

Частые ошибки

  • Verbose/Debug-логи в проде. Самый популярный способ незаметно потратить сотни долларов на телеметрию. В проде — Information и выше; подробности включайте точечно и временно.
  • Отключить sampling «чтобы видеть всё». На нагруженном сервисе объём вырастает в разы, а полезность — почти нет. Оставляйте адаптивную выборку.
  • Алерт без action group. Условие срабатывает, алерт зажигается в портале — и никто его не видит, потому что некому отправить. Проверяйте алерты хотя бы раз: временно занизьте порог и убедитесь, что письмо пришло.
  • Алерты на всё подряд. Сорок писем в день перестают читать через неделю, и настоящую аварию пропускают вместе со спамом. Алерт должен требовать действия — иначе это дашборд, а не алерт.
  • Только метрики платформы, без Application Insights. CPU в норме, память в норме, а пользователи видят ошибки: пятисотки отдаёт приложение, а не виртуалка.
  • Логировать персональные данные и секреты. Телеметрия хранится месяцами и доступна широкому кругу — токен или номер карты в traces превращается в инцидент безопасности.
  • Никакого availability-теста. Классика: истёк сертификат, снаружи сайт недоступен, внутри все метрики зелёные. Узнаёте от клиентов.

Итоги

  • Метрики говорят что сломалось (быстро и дёшево), логи — почему (медленнее и за деньги). Нужны оба.
  • Application Insights даёт то, чего нет у платформы: requests, dependencies, exceptions, traces вашего приложения.
  • KQL с конвейером | — рабочий инструмент расследования; путь «5xx → упавший эндпоинт → исключение → медленная зависимость» решает большинство инцидентов.
  • operation_Id связывает все события одного запроса — это самый быстрый способ понять, что произошло на самом деле.
  • Алерт живёт только вместе с action group; availability test ловит то, что не видно изнутри.
  • Приём телеметрии бесплатен примерно до 5 ГБ в месяц, дальше платно: следите за уровнем логирования, иначе мониторинг станет дороже приложения.
Проверьте себя
1. Приложение отдаёт 500-е ошибки. Какой сигнал быстрее всего покажет, что дело в медленной базе данных, а не в коде?
AМетрика CPU виртуальной машины App Service
BТаблица dependencies в Application Insights: там видны вызовы SQL, их длительность и таймауты
CСчётчик Http5xx в метриках платформы
DЛоги сборки в Azure Pipelines
2. Что чаще всего приводит к неожиданно большому счёту за Azure Monitor?
AСлишком много метрических алертов с частотой проверки в одну минуту
BВключённая адаптивная выборка телеметрии
CУровень логирования Debug или Verbose в продакшене — объём принимаемых логов растёт в разы
DХранение метрик платформы дольше 93 дней