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 ГБ в месяц, дальше платно: следите за уровнем логирования, иначе мониторинг станет дороже приложения.