---
course: ПиТПМ
lecture: 13
title: "Лекция №13. Инфраструктурный мониторинг развернутых модулей (Observability): Логирование, метрики и трассировка."
---

# Лекция №13. Инфраструктурный мониторинг развернутых модулей (Observability): Логирование, метрики и трассировка.

## §13.1. Почему тестировщик должен думать о мониторинге

Мы с вами прошли долгий путь. Научились **тестировать код, интеграции, интерфейсы, нагрузку, безопасность**. Научились **автоматизировать проверки в CI/CD**. Научились **искать утечки памяти** и **медленные методы**.

Но есть один вопрос, который мы пока не закрыли: *«А что происходит после того, как релиз вышел в продакшен?»*.

Тесты прошли. Конвейер зеленый. Релиз выкатили. Пользователи начали работать. И тут... **что-то пошло не так**.

Как узнать, что пошло не так? Как понять, что система работает нормально? Ответ: **мониторинг**.

Тесты проверяют до релиза. А что происходит после?

Весь курс был про **тестирование до релиза**. Мы проверяли код до того, как он попадет к пользователям. Но что происходит, когда пользователи уже работают?

| Что проверяли | Что происходит после релиза |
| --- | --- |
| **Функциональные тесты — проверяют логику** | Пользователи находят баги, которые мы не предусмотрели |
| **Нагрузочные тесты — проверяют производительность** | Нагрузка может быть выше, чем мы тестировали |
| **Интеграционные тесты — проверяют связи** | Внешний сервис может упасть или измениться |
| **Безопасность — проверяем уязвимости** | Появляются новые уязвимости, которых не было |

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

Вот, например, вы проверили машину перед выездом: давление в шинах, масло, тормоза. Всё хорошо. Вы выехали на трассу. Через час на дороге яма, вы пробили колесо. Как вы узнаете, что колесо спущено? Вы почувствуете вибрацию, услышите звук, увидите предупреждение на панели приборов. Это и есть **мониторинг** — наблюдение за системой в процессе работы.

**Мониторинг**

— это «тесты». на работающей системе Тесты мы запускаем до релиза. Мониторинг работает после релиза — постоянно, 24/7.

**Мониторинг**

— это наблюдение за работающей системой: сбор данных о том, что происходит, и анализ этих данных, чтобы понять, всё ли работает нормально.

Что **мониторинг** показывает:

| Что показывает | Пример |
| --- | --- |
| **Работает ли система?** | Сервер отвечает на запросы? |
| **Как быстро работает?** | Время ответа не выросло? |
| **Есть ли ошибки?** | Появились новые исключения в логах? |
| **Хватает ли ресурсов?** | Память не заканчивается? CPU не перегружен? |

**Мониторинг**

— это как приборная панель в самолете. Пилот не может проверить каждый винт перед полетом. Но он видит показатели на панели и понимает, всё ли в порядке.

**Тестирование** и **мониторинг** часто путают, хотя они решают разные задачи и работают на разных этапах жизни приложения. Наглядно сопоставить их ключевые отличия поможет таблица ниже:

| Тестирование | Мониторинг |
| --- | --- |
| **Проверяет до релиза** | Проверяет после релиза |
| **Запускается периодически** | Работает постоянно |
| **Проверяет конкретные сценарии** | Наблюдает за всем |
| **Находит баги** | Находит проблемы в работе |

**Мониторинг**

— это командная работа. Кто-то должен смотреть на данные и реагировать.

Кто участвует:

| Кто | Что делает |
| --- | --- |
| **Разработчик** | Пишет код, добавляет логи, исправляет ошибки, найденные через мониторинг |
| **Тестировщик** | Проверяет, что мониторинг показывает корректные данные, анализирует тренды, помогает находить проблемы |
| **DevOps / SRE** | Настраивает сбор метрик, строит дашборды, обеспечивает работу инфраструктуры |

Почему **тестировщи** должен участвовать в **мониторинге**:

`1.` Тестировщик знает систему лучше всех. Он тестировал её, знает, как она должна работать.

`2.` Тестировщик видит проблему со стороны пользователя. Если пользователь жалуется, тестировщик может проверить логи и понять, что произошло.

`3.` Тестировщик помогает улучшать мониторинг. Если в логах не хватает данных для анализа — тестировщик скажет разработчикам, что добавить.

`4.` Тестировщик — связующее звено. Он переводит технические данные (логи, метрики) на язык бизнеса («пользователи не могут оплатить заказ»).

Как же **мониторинг** помогает **тестировщику**? Ответить на этот вопрос поможет таблица, которая показана ниже:

| Ситуация | Без мониторинга | С мониторингом |
| --- | --- | --- |
| **Пользователь жалуется на ошибку** | Непонятно, что случилось | Смотрим логи → видим ошибку → понимаем, что произошло |
| **Система стала медленнее** | Пользователи жалуются, мы не знаем, почему | Смотрим метрики → видим, что время ответа выросло → ищем причину |
| **Сервер упал ночью** | Узнаем утром от пользователей | Уведомление приходит сразу → исправляем до того, как пользователи заметили |
| **Новый релиз сломал что-то** | Непонятно, что именно и где | Сравниваем метрики до и после релиза → видим, что изменилось |

В этой лекции мы разберем три столпа `Observability`:

`1.` **Логи** — записи о событиях (*«что случилось»*).

`2.` **Метрики** — числовые показатели (*«сколько чего»*).

`3.` **Трассировка** — путь запроса через систему (*«где случилось»*).

Простая аналогия:

- **Логи** — это как дневник событий («в 15:23 пользователь Иван нажал кнопку»).
- **Метрики** — это как приборная панель («сейчас 1000 пользователей, время ответа 200 мс»).
- **Трассировка** — это как трекинг посылки («запрос прошел через сервисы А, Б, В, в сервисе Б упал»).

## §13.2. Три столпа Observability: логи, метрики, трассы

**Observability (наблюдаемость)**

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

Представьте, что вы смотрите на работающий автомобиль. Вы не можете заглянуть внутрь двигателя. Но вы видите:

- Скорость на спидометре (метрики).
- Слышите звук двигателя и видите дым из выхлопной трубы (логи).
- Видите, что машина виляет вправо (трассировка).

Вы не заглядываете внутрь, но понимаете, что происходит. Это и есть **наблюдаемость**.

Представьте, что в офисе произошла кража. Вы — детектив. Вам нужно понять, что случилось.

| Что вы делаете | Аналогия с Observability |
| --- | --- |
| **Смотрите записи с камер** | Логи — записи событий |
| **Смотрите статистику: сколько людей заходило, во сколько** | Метрики — числовые показатели |
| **Восстанавливаете путь вора: вход → коридор → кабинет → выход** | Трассировка — путь запроса через систему |

Только вместе эти три источника дают полную картину.

**Столп №1. Логи (что случилось?)**

**Логи**

— это записи о событиях, которые происходят в системе. Каждое событие — это строчка в логе.

Что содержит **лог**:

wireframe

```
2024-01-15 15:23:45 [INFO] User Ivan logged in from IP 192.168.1.1

2024-01-15 15:23:47 [INFO] User Ivan created order #12345

2024-01-15 15:23:48 [ERROR] Payment failed: insufficient funds for user 456

2024-01-15 15:23:50 [INFO] User Ivan logged out
```

Что мы видим? Иван залогинился, создал заказ, оплата не прошла (недостаточно средств), вышел из системы.

| Ситуация | Что дают логи |
| --- | --- |
| **Ошибка в системе** | Видим, какая ошибка произошла |
| **Аудит** | Кто и что делал в системе |
| **Расследование инцидента** | Восстанавливаем последовательность событий |

**Столп №2. Метрики (сколько чего?)**

**Метрики**

— это числовые показатели работы системы. Это цифры, которые можно измерить.

Какие **метрики** собирают:

| Метрика | Что показывает | Пример |
| --- | --- | --- |
| **RPS** | Запросов в секунду | 100 RPS |
| **Latency** | Время ответа | 200 мс |
| **Error Rate** | Процент ошибок | 0.5% |
| **CPU** | Загрузка процессора | 75% |
| **Memory** | Использование памяти | 4 ГБ из 8 ГБ |

Как выглядят **метрики**:

wireframe

```
Метрики за последний час:

- RPS: 95 → 100 → 120 → 110 → 90

- Latency (p95): 150ms → 160ms → 200ms → 300ms → 450ms

- Error Rate: 0% → 0% → 0% → 0.5% → 2%
```

Что мы видим? Нагрузка **выросла** (*RPS*), время ответа увеличилось, появились ошибки. Возможно, **нагрузка слишком высокая**.

Когда **метрики** нужны:

| Ситуация | Что дают метрики |
| --- | --- |
| **Мониторинг в реальном времени** | Видим текущее состояние системы |
| **Обнаружение аномалий** | Видим, когда показатели выходят за норму |
| **Анализ трендов** | Видим, как меняется система со временем |

**Столп №3. Трассировка (где была проблема?)**

**Трассировка**

— это отслеживание пути одного запроса через все сервисы системы.

В **микросервисах** это особенно важно. Запрос может проходить через `5-10 сервисов`. Если что-то упало — нужно понять, в каком именно.

Пример **трассировки**:

bash

```bash
Запрос: GET /api/order/12345
├── API Gateway (5ms)
│   ├── Auth Service (2ms)
│   │   └── Проверка токена ✅
│   ├── Order Service (15ms)
│   │   ├── Проверка заказа ✅
│   │   └── Запрос к БД (10ms) ✅
│   ├── Payment Service (FAILED)
│   │   ├── Проверка статуса платежа ✅
│   │   └── Запрос к платежному шлюзу ❌ ТАЙМАУТ!
│   └── User Service (3ms)
│       └── Получение данных пользователя ✅
└── Ответ пользователю: 500 Internal Server Error
```

Что мы видим? Запрос прошел через `4 сервиса`. В `Payment Service` произошел **таймаут** при запросе к платежному шлюзу. Вот где проблема!

Когда **трассировка** нужна:

| Ситуация | Что дает трассировка |
| --- | --- |
| **Запрос упал в микросервисах** | Видим, какой сервис упал |
| **Запрос выполняется долго** | Видим, какой сервис тормозит |
| **Расследование инцидента** | Восстанавливаем путь запроса |

Когда в системе случается сбой, разработчик превращается в детектива. Разберем наглядный пример классического расследования, где три столпа — **метрики**, **логи** и **трассировка** — помогают пошагово выйти на след проблемы. Представим ситуацию – пользователи жалуются, что заказы не проходят.

**Шаг №1. Смотрим метрики**

wireframe

```
Error Rate: 0% → 0.5% → 2% → 5% (растет!)
```

**Вывод**: что-то пошло не так. Проблема началась примерно `15 минут назад`.

**Шаг№ 2. Смотрим логи**

wireframe

```
15:23:48 [ERROR] Payment failed: timeout calling payment gateway

15:24:12 [ERROR] Payment failed: timeout calling payment gateway

15:24:56 [ERROR] Payment failed: timeout calling payment gateway
```

**Вывод**: Платежный шлюз не отвечает. Ошибка в `Payment Service`.

**Шаг №3. Смотрим трассировку**

bash

```bash
Запрос: POST /api/order
├── API Gateway (5ms) ✅
├── Order Service (10ms) ✅
├── Payment Service (TIMEOUT)
│   └── Запрос к платежному шлюзу ❌ ТАЙМАУТ!
└── Response: 500 Internal Server Error
```

**Вывод**: Платежный шлюз не отвечает. Нужно проверить его доступность.

**Метрики** показали рост ошибок. **Логи** показали конкретную ошибку. **Трассировка** показала, где именно (*в Payment Service*). Всё вместе — понятно, что делать.

## §13.3. Логирование: запись событий

**Логи**

— это записи о том, что происходило в системе. Каждое событие — одна строчка в логе.

**Логи**

— это как дневник, который ведет программа. В нём записано всё, что с ней происходило: кто приходил, что делал, что сломалось.

**Логи** незаменимы при разборе инцидентов, когда нужно восстановить картину произошедшего по шагам. Вот как это работает на практике при появлении ошибки в системе.

В **логах** есть уровни. Они показывают, насколько важное событие произошло.

| Уровень | Когда использовать | Пример |
| --- | --- | --- |
| **Debug** | Для отладки (только в разработке) | [DEBUG] UserService.GetUser called with id=123 |
| **Info** | Обычные события | [INFO] User Ivan logged in |
| **Warning** | Что-то не так, но не критично | [WARNING] Database connection slow: 200ms |
| **Error** | Ошибка, но система работает | [ERROR] Payment failed: insufficient funds |
| **Critical** | Система может упасть | [CRITICAL] Database connection lost! |

Простая аналогия:

- **Debug** — ваши заметки (никто не читает, только вы).
- **Info** — новости («Иван зашел в офис»).
- **Warning** — «В офисе стало душно, нужно открыть окно».
- **Error** — «Сломался принтер, но работа продолжается».
- **Critical** — «Пожар в здании, нужно эвакуироваться».

Чтобы **логи** не превращались в сплошной и бесполезный поток текста, важно строго разделять события по степени их важности. Понять, какому событию какой уровень **логгирования** соответствует на практике, поможет таблица ниже:

| Ситуация | Какой уровень |
| --- | --- |
| **«Запрос пришел»** | Info |
| **«Пользователь создал заказ»** | Info |
| **«База данных ответила медленно»** | Warning |
| **«Платеж не прошел»** | Error |
| **«Сервер не отвечает»** | Critical |
| **«Внутренняя логика метода»** | Debug |

В продакшене включают **Info** и выше. **Debug** — только на **тестовой среде**, потому что его слишком много.

**Хороший лог** содержит всю информацию, чтобы понять, что произошло. Что должно быть в каждой записи:

| Что | Зачем | Пример |
| --- | --- | --- |
| **Время** | Когда произошло | 2024-01-15 15:23:48 |
| **Уровень** | Насколько важно | [ERROR] |
| **Сообщение** | Что произошло | Payment failed: insufficient funds |
| **Контекст** | Кто, что, где | User Ivan (ID: 456), Order #12345 |

Плохой **лог**:

wireframe

```
Error occurred
```

Хороший **лог**:

wireframe

```
2024-01-15 15:23:48 [ERROR] Payment failed for user id=456, order #12345: insufficient funds, payment gateway response: decline
```

В хорошем **логе** есть всё, чтобы понять проблему без дополнительных вопросов. Пример кода (*C# с Serilog*):

csharp

```csharp
public class OrderService
{
    private readonly ILogger _logger;
    public OrderService(ILogger logger)
        => _logger = logger;
    public async Task<bool> ProcessOrder(Order order)
    {
        _logger.Information("Processing order {OrderId} for user {UserId}", 
                            order.Id, 
                            order.UserId);
        
        try
        {
            var result = await _paymentService.Pay(order);
            if (result.Success)
            {
                _logger.Information("Payment succeeded for order {OrderId}", 
                                    order.Id);
                return true;
            }
            else
            {
                _logger.Warning("Payment declined for order {OrderId}: {Reason}",    
                                order.Id, 
                                result.Reason);
                return false;
            }
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Payment failed for order {OrderId}", 
                          order.Id);
            throw;
        }
    }
}
```

Что мы получаем:

wireframe

```
[INFO] Processing order 12345 for user 456

[INFO] Payment succeeded for order 12345
```

Или если ошибка:

wireframe

```
[ERROR] Payment failed for order 12345: System.TimeoutException: Payment gateway timeout
```

Тестировщик смотрит на эти **логи** и понимает, что произошло.

Место хранения **логов** напрямую зависит от того, на каком этапе **жизненного цикла** находится приложение. Сориентироваться в том, где искать диагностические данные для разных сред, поможет следующая таблица:

| Среда | Где логи |
| --- | --- |
| **Разработка (локально)** | В консоли или в файле logs/app.log |
| **Тестовая среда (staging)** | В файлах на сервере или в ELK/Splunk |
| **Продакшен** | В централизованной системе (ELK, Application Insights, Splunk) |

В продакшене **логи** хранятся в **централизованном месте**. Не нужно заходить на каждый сервер — можно посмотреть всё в одном месте.

## §13.4. Метрики: измерения производительности

**Метрики**

— это числовые показатели работы системы. Это цифры, которые можно измерить, записать и проанализировать.

**Метрики**

— это приборная панель в машине. Вы видите скорость, обороты двигателя, уровень топлива, температуру. Вы не смотрите под капот, но понимаете, что происходит с машиной. Пример **метрик**:

Текущее состояние системы:

wireframe

```
- Запросов в секунду: 150

- Среднее время ответа: 200 мс

- Процент ошибок: 0.5%

- Загрузка CPU: 65%

- Использование памяти: 4.2 ГБ из 8 ГБ
```

**Метрики** делятся на несколько категорий. Вот самые важные:

**1. Метрики нагрузки (сколько запросов)**

| Метрика | Что показывает | Пример |
| --- | --- | --- |
| **RPS (Requests Per Second)** | Количество запросов в секунду | 150 RPS |
| **Active Users** | Сколько пользователей сейчас работает | 1 234 пользователя |
| **Throughput** | Сколько данных передается | 50 МБ/с |

Эти **метрики** необходимо смотреть тогда, когда нужно понять, насколько **нагружена система**.

**2. Метрики производительности (как быстро)**

| Метрика | Что показывает | Пример |
| --- | --- | --- |
| **Latency (p50, p95, p99)** | Время ответа в процентилях | p95 = 300 мс |
| **TTFB (Time To First Byte)** | Время до первого байта | 150 мс |
| **Response Time** | Полное время ответа | 200 мс |

Эти **метрики** необходимо смотреть тогда, когда нужно понять, **не стала ли система медленнее**.

**3. Метрики ошибок (всё ли хорошо)**

| Метрика | Что показывает | Пример |
| --- | --- | --- |
| **Error Rate** | Процент запросов с ошибками | 0.5% |
| **5xx Errors** | Количество серверных ошибок | 12 в минуту |
| **4xx Errors** | Количество клиентских ошибок | 5 в минуту |

Эти **метрики** необходимо смотреть тогда, когда нужно понять, не начала ли система падать.

**4. Метрики ресурсов (хватает ли мощности)**

| Метрика | Что показывает | Пример |
| --- | --- | --- |
| **CPU Usage** | Загрузка процессора | 65% |
| **Memory Usage** | Использование памяти | 4.2 ГБ |
| **Disk Usage** | Использование диска | 200 ГБ из 500 ГБ |
| **Network I/O** | Сетевой трафик | 50 МБ/с |

Эти **метрики** необходимо смотреть тогда, когда нужно понять, **не нужно ли добавить ресурсов**.

График **метрик** за час выглядит примерно следующим образом:

![](/images/lectures/pitpm/13/image-01.webp)

Что мы видим:

- В 15:20 нагрузка выросла (RPS) → время ответа увеличилось (Latency) → появились ошибки (Error Rate).
- В 15:40 нагрузка упала → время ответа вернулось к норме → ошибки исчезли.

Система **не справляется с пиковой нагрузкой** в 15:20. Нужно либо оптимизировать код, либо добавить ресурсы.

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

Простая аналогия:

- **Профилирование** — это как МРТ (разовый, глубокий анализ).
- **Метрики** — это как термометр и тонометр (постоянный, поверхностный контроль).

Сам по себе сбор цифр не имеет смысла, если их неудобно анализировать. Для построения полноценной системы мониторинга используется **классический стек инструментов**, чьи роли распределены в таблице ниже:

| Инструмент | Что делает |
| --- | --- |
| **Prometheus** | Сбор и хранение метрик |
| **Grafana** | Визуализация метрик (графики, дашборды) |
| **Application Insights** | Мониторинг для .NET (Azure) |
| **CloudWatch** | Мониторинг для AWS |

**Метрики** нужны не только **разработчикам** и **системным администраторам** для круглосуточного дежурства, но и **тестировщикам**. Понять, как **QA-инженер** использует графики и цифры на разных этапах своей работы, поможет таблица ниже:

| Что делает тестировщик | Как метрики помогают |
| --- | --- |
| **Проверяет работу системы после релиза** | Смотрит, не выросли ли ошибки после релиза |
| **Анализирует проблемы пользователей** | Видит, когда началась проблема (по графику) |
| **Планирует нагрузочные тесты** | Видит пиковые нагрузки и тестирует именно их |
| **Проверяет, что оптимизация сработала** | Видит, упало ли время ответа после оптимизации |

## §13.5. Трассировка: отслеживание запроса по всей системе

**Трассировка**

— это отслеживание пути одного запроса через все сервисы системы.

Простая аналогия: **трассировка** — это как трекинг посылки. Вы видите, где посылка:

- «Покинула склад».
- «Прибыла в сортировочный центр».
- «Отправлена в ваш город».
- «Задержана на таможне».

Вы точно знаете, на каком этапе проблема.

В **микросервисах** **трассировка** становится **критически важной**. Запрос может проходить через `5`, `10`, даже `20 сервисов`. Если что-то упало — без трассировки непонятно, где именно.

Почему трассировка нужна именно в **микросервисах**? В **монолите всё просто**: *«Запрос → Монолитное приложение → Ответ»*.

Если что-то упало — ошибка в одном месте. Понятно, где искать.

В **микросервисах** сложно: *«Запрос → API Gateway → Сервис А → Сервис Б → Сервис В → Сервис Г → Ответ»*.

Если что-то упало — непонятно, в каком из `5 сервисов` ошибка.

Простая аналогия:

- **Монолит** — это как один магазин. Если товара нет — понятно, что в этом магазине.
- **Микросервисы** — это как сеть магазинов. Заказ проходит через склад, логистику, доставку. Если заказ не пришел — непонятно, где проблема: на складе, в логистике или у курьера.

Чтобы увидеть, сколько времени уходит на каждый внутренний вызов системы при обработке одного действия, используют **распределенную трассировку**. Посмотрим на примере, как выглядит путь клиентского запроса *«создать заказ»*:

bash

```bash
Запрос: POST /api/order
Trace ID: abc-123-def-456

├── API Gateway (5ms)
│   ├── Проверка токена (2ms) ✅
│   └── Маршрутизация (3ms) ✅
│
├── Auth Service (3ms)
│   └── Проверка прав пользователя (3ms) ✅
│
├── Order Service (15ms)
│   ├── Проверка заказа (5ms) ✅
│   └── Запрос к БД (10ms) ✅
│
├── Payment Service (FAILED)
│   ├── Проверка статуса платежа (2ms) ✅
│   └── Запрос к платежному шлюзу (TIMEOUT) ❌
│
└── User Service (4ms)
    └── Получение данных пользователя (4ms) ✅

Результат: 500 Internal Server Error
Проблема: Payment Service → таймаут при запросе к платежному шлюзу
```

Что мы видим:

- Запрос прошел через 5 сервисов.
- В 4 сервисах всё хорошо.
- В Payment Service произошел таймаут.
- Мы точно знаем, где проблема!

Чтобы **сквозной мониторин**г работал **корректно**, каждый запрос обрастает специальными **служебными метками**. Сводка основных компонентов трассировки с примерами и назначением представлена в таблице:

| Что | Пример | Зачем |
| --- | --- | --- |
| **Trace ID** | abc-123-def-456 | Идентификатор всего запроса. Одинаковый для всех сервисов |
| **Span ID** | span-001, span-002 | Идентификатор одного шага внутри сервиса |
| **Service Name** | Payment Service | Название сервиса |
| **Operation** | Запрос к платежному шлюзу | Что делали |
| **Duration** | TIMEOUT или 2ms | Сколько времени заняло |
| **Status** | ✅ или ❌ | Успешно или ошибка |

**Трассировка** превращает долгие поиски **виноватого микросервиса** в **точечную диагностику**. Посмотрим на реальных примерах, как она решает проблемы с ошибками, медленной работой и расследованием инцидентов.

Чтобы собирать и анализировать **пути запросов между сервисами**, применяются как независимые **open-source системы**, так и **комплексные облачные решения**. Посмотрим на ключевые инструменты:

| Инструмент | Что делает |
| --- | --- |
| **Jaeger** | Популярный инструмент для трассировки (OpenTracing) |
| **Zipkin** | Еще один инструмент для трассировки |
| **Application Insights** | Трассировка + логи + метрики в Azure |
| **OpenTelemetry** | Стандарт для сбора трассировок |

В руках **тестировщика трассировка** превращается в **инструмент** сквозного контроля **микросервисной архитектуры**.

Посмотрим, как она помогает находить **сбои интеграции и медленные участки**:

| Что делает тестировщик | Как трассировка помогает |
| --- | --- |
| Находит, какой сервис упал | Видит в трассировке, где ошибка |
| Проверяет, что запрос прошел все сервисы | Видит полный путь запроса |
| Анализирует медленные запросы | Видит, какой сервис тормозит |
| Проверяет корректность интеграции | Видит, что запрос прошел через все нужные сервисы |

## §13.6. Логи + Метрики + Трассы = Полная картина

**Логи, метрики и трассировка**

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

Простая аналогия – это детективное расследование.

| Инструмент | Что дает | Аналогия в детективе |
| --- | --- | --- |
| **Метрики** | «Что-то пошло не так» | «В доме кто-то был» |
| **Трассировка** | «Где именно» | «Вор прошел через окно» |
| **Логи** | «Что именно случилось» | «Вор украл картину» |

По отдельности **каждый тип данны**х отвечает лишь на **часть вопросов**, но вместе они дают п**олную картину** состояния системы. Посмотрим, как **логи, метрики и трассировка** дополняют друг друга на практике.

Чтобы закрепить концепцию `Observability` на практике, проследим за слаженной работой всех трех компонентов в реальном сценарии. Увидеть пошаговый алгоритм расследования сложного инцидента от первого сигнала до точной причины поможет разбор ниже.

**Шаг №1. Метрики показали проблему**

wireframe

```
Метрики за последние 10 минут:

- Error Rate: 0% → 5% (резкий рост!)

- RPS: 100 → 200 (нагрузка выросла)

- Latency: 200ms → 500ms (время ответа выросло)

Вывод: Что-то пошло не так. Началось примерно в 15:20.
```

**Шаг №2. Трассировка показала, где**

wireframe

```
Trace ID: abc-123-def-456

Запрос: POST /api/order (15:23:45)

├── API Gateway (5ms) ✅

├── Auth Service (3ms) ✅

├── Order Service (15ms) ✅

├── Payment Service (FAILED) ❌ ← ПРОБЛЕМА ЗДЕСЬ!

└── User Service (4ms) ✅

Вывод: Проблема в Payment Service. Запрос дошел до него и упал.
```

**Шаг №3. Логи показали, почему**

wireframe

```
Логи Payment Service в 15:23:45:

[ERROR] Payment failed for order #12345: insufficient funds

[ERROR] User 456 has balance 50.00, attempted to pay 100.00

[WARNING] Order #12345 status changed to "payment_failed"
```

Какой мы можем сделать вывод? У пользователя *недостаточно средств*. **Ошибка не в системе, а в данных**.

Итоговая схема наглядно объединяет все три компонента `Observability` в единый замкнутый контур. Увидеть, как метрики, трассировка и логи собираются в единую карту инцидента, поможет итоговая визуализация ниже:

![](/images/lectures/pitpm/13/image-02.webp)

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

Тестировщик:

`1.` Смотрю метрики. `Error Rate` вырос с `0%` до `5%` в последние `15 минут`. Значит, проблема массовая, не у одного пользователя.

`2.` Смотрю трассировку. Нахожу один из упавших запросов. Вижу, что проблема в `Payment Service` — запрос дошел до него и упал.

`3.` Смотрю логи в Payment Service. Вижу конкретную ошибку: *«insufficient funds»*. У пользователя недостаточно средств.

`4.` Пишу отчет: *«Проблема не в системе. Пользователи пытаются оплатить заказы, но у них недостаточно средств. Возможно, в приложении не отображается ошибка понятно для пользователя. Нужно улучшить сообщение об ошибке.»*.

Чтобы всегда держать в голове назначение каждого инструмента, удобно использовать **простую матрицу соответствия** вопросов и ответов. Сводная таблица ниже наглядно закрепляет эту логику представлена ниже:

| Инструмент | Вопрос | Ответ |
| --- | --- | --- |
| **Метрики** | «Что-то не так?» | «Да, ошибок стало больше» |
| **Трассировка** | «Где?» | «В Payment Service» |
| **Логи** | «Почему?» | «Недостаточно средств» |

Без одного из них — картина неполная.

| Чего нет | Что теряем |
| --- | --- |
| **Нет метрик** | Не знаем, что проблема началась |
| **Нет трассировки** | Не знаем, в каком сервисе проблема |
| **Нет логов** | Не знаем, почему проблема произошла |

## §13.7. Инструменты мониторинга для .NET

В мире `.NET` есть несколько инструментов для мониторинга. Они делятся на две категории:

| Категория | Что делают | Примеры |
| --- | --- | --- |
| **Библиотеки для кода** | Добавляют логи, метрики, трассировку в код | Serilog, OpenTelemetry |
| **Системы для сбора и хранения** | Собирают и хранят данные | ELK Stack, Prometheus, Application Insights |
| **Системы для визуализации** | Показывают данные в виде графиков | Grafana, Kibana, Application Insights |

Простая аналогия:

- **Библиотека (Serilog)** — это как ручка, которой вы пишете.
- **Система сбора (ELK, Prometheus)** — это как сейф, где вы храните записи.
- **Система визуализации (Grafana, Kibana)** — это как стол, на котором вы раскладываете записи, чтобы их прочитать.

Переходя от теоретических основ `Observability` к практической реализации в экосистеме `.NET`, начнем с инструментов логирования.

**Библиотека №1. Serilog**

**Serilog**

- это популярная библиотека для логирования в `.NET`. Она простая и гибкая.

Как она выглядит:

csharp

```csharp
using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("logs/app.log")
    .CreateLogger();
Log.Information("User {UserId} created order {OrderId}", 
                userId, orderId);
Log.Warning("Payment declined for order {OrderId}: {Reason}", 
            orderId, reason);
Log.Error(ex, "Payment failed for order {OrderId}", orderId);
```

Что умеет данная **библиотека**:

| Возможность | Что делает |
| --- | --- |
| **Запись в разные места** | Консоль, файл, база данных, облако |
| **Структурированные логи** | Логи в формате JSON, легко искать |
| **Уровни логирования** | Debug, Info, Warning, Error, Critical |

**Библиотека №2. OpenTelemetry**

**OpenTelemetry**

- это стандарт для сбора логов, метрик и трассировки. Поддерживается многими инструментами.

Вместо того чтобы использовать **разные библиотеки для логов, метрик и трассировки**, вы используете один стандарт. И данные можно отправлять в **любую систему** (*Prometheus, Jaeger, Application Insights*). Как это выглядит:

csharp

```csharp
using OpenTelemetry;
using OpenTelemetry.Trace;

var tracer = tracerProvider.GetTracer("MyApp");

using var span = tracer.StartActiveSpan("ProcessOrder");
span.SetAttribute("order.id", orderId);

try
{
    // ... логика ...
    span.SetStatus(Status.Ok);
}
catch (Exception ex)
{
    span.SetStatus(Status.Error);
    span.RecordException(ex);
    throw;
}
```

Мало **собрать** и **записать логи в коде** — централизованная система наблюдаемости **требует надежного инфраструктурного бэкенда** для их хранения и анализа. Рассмотреть классический стек для работы с логами, компоненты которого закрывают весь путь от сбора до визуализации, можно в разборе ниже.

**ELK Stack (Elasticsearch + Logstash + Kibana)**

Это три инструмента, которые всегда работают вместе.

| Инструмент | Что делает |
| --- | --- |
| **Elasticsearch** | Хранит и индексирует логи |
| **Logstash** | Собирает логи из разных источников |
| **Kibana** | Визуализация и поиск по логам |

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

| Возможность | Что делает |
| --- | --- |
| **Поиск по логам** | Найти все логи по пользователю, ошибке, времени |
| **Фильтрация** | Показать только ошибки за последний час |
| **Агрегация** | Сколько ошибок в час, какие ошибки чаще всего |

Для работы с метриками используется промышленный стандарт мониторинга — связка **Prometheus** и **Grafana**. Изучить разделение ролей между сборщиком числовых данных и инструментом их визуализации можно в таблице ниже.

| Инструмент | Что делает |
| --- | --- |
| **Prometheus** | Собирает и хранит метрики |
| **Grafana** | Показывает метрики в виде графиков |

Так же инструмент **Grafana** имеет следующие возможности:

| Возможность | Что делает |
| --- | --- |
| **Дашборды** | Графики, таблицы, показатели на одной странице |
| **Алерты** | Уведомления, если метрика выходит за норму |
| **Множество источников** | Prometheus, Elasticsearch, SQL, и другие |

Чтобы оценить, как вся собранная телеметрия выглядит на практике для инженера или тестировщика, рассмотрим визуальное представление дашборда мониторинга. Увидеть пример того, как метрики преобразуются в наглядные панели управления в Grafana, можно на схеме ниже.

![](/images/lectures/pitpm/13/image-03.webp)

## §13.8. Интеграция с CI/CD: мониторинг как часть релиза

Мы привыкли думать, что **мониторинг — это то, что делают после релиза. Запустили код, смотрим на графики**.

Но на самом деле **мониторинг** должен быть частью процесса релиза. **Релиз не заканчивается в момент деплоя**. Релиз заканчивается, когда мы убедились, что система работает нормально.

**Интеграция наблюдаемости** в процесс **CI/CD** превращает **мониторинг** в активный инструмент управления качеством. Рассмотрим схему взаимодействия релизного цикла и контура обратной связи по метрикам.

![](/images/lectures/pitpm/13/image-04.webp)

**Интеграция** наблюдаемости в **пайплайн доставки ПО** превращает каждый релиз в к**онтролируемую процедуру с четкими критериями успешности**. Изучим ключевые этапы этого процесса — от **CI/CD** до **продакшена и процедур отката**.

| Этап | Что происходит |
| --- | --- |
| **1. Тесты прошли** | CI/CD подтвердил, что код работает |
| **2. Релиз на staging** | Код развернут на тестовой среде |
| **3. Наблюдение (1 час)** | Смотрим на метрики, нет ли аномалий |
| **4. Релиз на продакшен** | Если всё хорошо — выкатываем |
| **5. Наблюдение (еще час)** | Снова смотрим, нет ли проблем после релиза |
| **6. Если проблема — откат** | Возвращаемся к предыдущей версии |

Самое важное правило

«Если после релиза ошибок стало больше — откатываем. Если после релиза метрики ухудшились — откатываем релиз.».

Признаки проблемы:

| Что видим в метриках | Что это значит |
| --- | --- |
| **Error Rate вырос с 0% до 5%** | Система начала падать |
| **Latency выросла с 200 мс до 800 мс** | Система стала медленнее |
| **CPU вырос до 95%** | Система перегружена |

В случае возникновения подобных проблем необходимо следовать следующим шагам:

**Шаг №1. Заметили рост ошибок.**

**Шаг №2. Смотрим логи и трассировку (понимаем, что именно упало).**

**Шаг №3. Если проблема не исправляется быстро — откатываем релиз.**

**Шаг №4. Релиз откачен, ошибки исчезли.**

**Шаг №5. Разбираемся с проблемой в спокойной обстановке.**

Это крайне важно, ведь если не **откатить** — пользователи у**видят ошибки, система упадет, бизнес потеряет деньги**.

Не все проблемы — это ошибки. Иногда система работает, но медленно. Представим следующий сценарий: *«До релиза: Latency = 200 мс. После релиза: Latency = 500 мс.»*.

Что делаем:

`1.` Смотрим на график `Latency`. Когда начался рост? Сразу после релиза или постепенно?

`2.` Смотрим на график `RPS`. Нагрузка выросла или осталась той же? Если нагрузка та же — проблема в коде.

`3.` Смотрим на `CPU` и `Memory`. Они выросли? Возможно, код стал потреблять больше ресурсов.

`4.` Смотрим на **логи**. Нет ли новых медленных запросов?

`5.` Находим причину и исправляем.

В **CI/CD** мы научились автоматически запускать тесты при каждом `push`. Теперь мы добавляем **мониторинг в этот процесс**. Как это выглядит:

yaml

```yaml
# GitHub Actions (упрощенно)
name: Release

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging
        run: deploy-staging.sh
      
      - name: Wait for stability (5 minutes)
        run: sleep 300
      
      - name: Check metrics
        run: check-metrics.sh
      
      - name: Deploy to production
        run: deploy-prod.sh
      
      - name: Wait for stability (5 minutes)
        run: sleep 300
      
      - name: Check metrics again
        run: check-metrics.sh
```

**Автоматизация проверки состояния** сервиса **непосредственно** внутри **конвейера** доставки исключает человеческий фактор при принятии решений о релизе. Изучить пошаговый алгоритм автоматического контроля метрик на этапах `staging` и `production` можно в таблице ниже:

| Этап | Что происходит |
| --- | --- |
| **Deploy to staging** | Выкатываем на тестовую среду |
| **Wait for stability** | Даем системе «прогреться» (5 минут) |
| **Check metrics** | Проверяем Error Rate, Latency, CPU |
| **Если метрики плохие** | Конвейер падает, релиз не идет дальше |
| **Deploy to production** | Если всё хорошо — выкатываем на продакшен |
| **Check metrics again** | Проверяем снова после релиза |

**Профилировани**е и **мониторинг** работают вместе:

| Инструмент | Что делает | Когда |
| --- | --- | --- |
| **Мониторинг** | Постоянно смотрит на систему | Всегда |
| **Профилирование** | Глубоко анализирует конкретную проблему | Когда мониторинг показал проблему |

Завершает цикл работы с **производительностью** непрерывный контур обратной связи, где данные мониторинга переходят в задачи для оптимизации кода. Проследить **полный жизненный цикл** устранения узких мест — от обнаружения проблемы до подтверждения исправления — можно на схеме ниже.

![](/images/lectures/pitpm/13/image-05.webp)

Лучше всего суть `Observability` раскрывается на живых примерах. Посмотрим, как связка метрик, профилирования и исправления кода возвращает системе нормальную скорость работы.

wireframe

```
Мониторинг показал: Latency выросла с 200 мс до 500 мс.

Профилирование показало: метод SaveOrder() стал выполняться 2 секунды.

Разработчик исправил: оптимизировал запрос к БД.

Мониторинг снова показывает: Latency 200 мс.
```

Когда **аномалия** обнаружена прямо на **продакшене**, критически важно действовать по четкому алгоритму **инцидент-менеджмента**. Рассмотреть пошаговый план реагирования на **деградацию метрик** после **выката новой версии** можно в таблице ниже:

| Шаг | Что делаем |
| --- | --- |
| 1 | Смотрим метрики: что именно ухудшилось? (Error Rate, Latency, CPU) |
| 2 | Смотрим логи: какая конкретная ошибка? |
| 3 | Смотрим трассировку: в каком сервисе проблема? |
| 4 | Принимаем решение: исправляем или откатываем |
| 5 | Если откатываем — возвращаемся к предыдущей версии |
| 6 | Если исправляем — делаем фикс и выкатываем снова |
