> ## Documentation Index
> Fetch the complete documentation index at: https://veriqa.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Наблюдаемость

> Метрики, трассы, логи и health-эндпоинты Veriqa и их экспорт через OpenTelemetry.

Veriqa инструментирован стандартными примитивами .NET — `System.Diagnostics.Metrics` и
`System.Diagnostics.ActivitySource` — и сам ничего никуда не экспортирует. Инструменты принадлежат
компонентам; хост подписывается на них и решает, куда уходят данные. Имена на этой странице —
публичный контракт: стройте на них дашборды и алерты.

| Сигнал | Имя для подписки |
| - | - |
| Метрики (meters) | `Veriqa.Core.TransactionEngine`, `Veriqa.Core.ChannelAdapter` |
| Трассы (activity sources) | `Veriqa.Core.TransactionEngine`, `Veriqa.Core.ChannelAdapter` |

## Включить экспорт

<Tabs>
  <Tab title="Self-hosted образ">
    Образ уже подписан на оба метра и оба источника спанов, а также на инструментацию ASP.NET Core,
    `HttpClient` и рантайма .NET. Экспорт включается, когда стандартная переменная OpenTelemetry
    указывает на коллектор:

    ```bash theme={null}
    OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
    ```

    Без переменной ничего не экспортируется. С ней метрики, трассы и логи уходят по OTLP; остальные
    стандартные переменные `OTEL_EXPORTER_OTLP_*` (протокол, заголовки) и `OTEL_SERVICE_NAME`
    читает сам OpenTelemetry SDK, как обычно.

    Собственного scrape-эндпоинта у образа нет — `/metrics` не существует. Для Prometheus
    направьте образ на OpenTelemetry Collector, а метрики отдавайте или отправляйте уже из него.
  </Tab>

  <Tab title="Встроен в ваш хост">
    `AddVeriqaAuthServer` регистрирует инструменты и больше ничего: конвейер OpenTelemetry —
    вашего хоста. Добавьте в него два имени; константы лежат в пакете `Veriqa.Core.Contracts`
    (лицензия MIT):

    ```csharp theme={null}
    using OpenTelemetry.Metrics;
    using OpenTelemetry.Trace;
    using Veriqa.Core.ChannelAdapter.Constants;
    using Veriqa.Core.TransactionEngine.Constants;

    builder.Services.AddOpenTelemetry()
        .WithMetrics(metrics => metrics
            .AddMeter(TransactionTelemetry.MeterName)
            .AddMeter(ChannelTelemetry.MeterName))
        .WithTracing(tracing => tracing
            .AddSource(TransactionTelemetry.ActivitySourceName)
            .AddSource(ChannelTelemetry.ActivitySourceName));
    ```

    Экспортеры, сэмплирование и инструментацию ASP.NET Core выбираете вы. Хост без конвейера
    OpenTelemetry за инструменты ничем не платит и данных не получает.
  </Tab>
</Tabs>

## Метрики

### Транзакции — метр `Veriqa.Core.TransactionEngine`

| Метрика | Тип | Единица | Теги |
| - | - | - | - |
| `veriqa.transaction.initiated` | counter | — | `veriqa.transaction.type` |
| `veriqa.transaction.terminated` | counter | — | `veriqa.transaction.outcome`, `channel_type` |
| `veriqa.transaction.duration` | histogram | `s` | `veriqa.transaction.outcome` |

* **`initiated`** считает транзакции, вошедшие в жизненный цикл; **`terminated`** — дошедшие до
  терминального состояния, каждую один раз: повторный терминальный переход дважды не считается.
* **`duration`** — время жизни от создания до терминального состояния. Когда в цикле человек, его
  определяет то, как долго он отвечает, а не сервер.
* Воронка входа — `initiated` против `terminated` с исходом `completed`; доля отказов — доля
  остальных исходов, кроме `superseded`: это повторная попытка, а не отказ, и она считается отдельно
  от `failed`, чтобы повторы не поднимали долю сбоев.

| Тег | Значения |
| - | - |
| `veriqa.transaction.type` | `login` — вход; `confirmation` — [серверное подтверждение](/docs/ru/reference/confirmation-api) |
| `veriqa.transaction.outcome` | `completed` — подтверждена и завершена; `declined` — пользователь отказал; `expired` — нет ответа за TTL; `failed` — сбой, не являющийся отказом пользователя; `superseded` — вход закрыт, потому что тот же пользователь [начал новый](/docs/ru/concepts/auth-flow#повторный-вход): в событиях и журнале аудита это транзакция `Failed` с причиной `superseded_by_new_sign_in` |
| `channel_type` | Тип канала, который ответил, иначе — канала, который был запрошен; `unknown`, когда транзакция завершилась без участия канала — как правило, истекла до чьего-либо ответа |

<Note>
  `completed` — намеренно не `confirmed`. Пользователю в мессенджере показывается «подтверждено»;
  движок записывает `completed` шагом позже, когда личность разрешена и сохранена. Поэтому вход,
  подтверждённый в канале, всё ещё может закончиться исходом `failed`.
</Note>

### Каналы — метр `Veriqa.Core.ChannelAdapter`

| Метрика | Тип | Теги | Что считает |
| - | - | - | - |
| `veriqa.channel.notice.failures` | counter | `channel_type`, `error_code`, `notice_kind` | Сообщения, которые канал не смог доставить пользователю |
| `veriqa.channel.start.unserved` | counter | `channel_type` | События начала входа, которые ядро не смогло обслужить: названной транзакции уже нет, её окно закрылось или она больше не принимает событие |
| `veriqa.channel.event.foreign_transaction` | counter | `channel_type` | События канала, называющие транзакцию чужого тенанта — не того, кому принадлежит регистрация канала, через которую они пришли |

* **`notice.failures`** — первый кандидат на алерт: растущий ряд означает, что пользователи не
  получают запрос подтверждения (`notice_kind` = `message`) или итоговый исход (`notice_kind` =
  `outcome`). `error_code` — код ошибки адаптера, всегда из реестра кодов.
* **`start.unserved`** делает видимой тишину. Пользователь открыл устаревшую ссылку или отсканировал
  старый QR, а канал без настроенного текста ответа не отвечает ничего — счётчик всё равно растёт.
* **`event.foreign_transaction`** — отправителю отвечают ровно так же, как на неизвестную
  транзакцию, поэтому ответ ничего не раскрывает. Устойчивый поток — кто-то перебирает чужие
  идентификаторы.

<Note>
  **В тегах метрик нет идентификаторов.** Теги не несут идентификаторов транзакции, сессии и
  пользователя канала, получателя и текста сообщения. Каждый тег — закрытый перечень, поэтому
  число временных рядов ограничено вашими каналами и значениями выше.
</Note>

## Трассы

| Спан | Источник | Когда открывается |
| - | - | - |
| `veriqa.transaction.create` | `Veriqa.Core.TransactionEngine` | Создание транзакции — корень трассы входа |
| `veriqa.channel.prompt.send` | `Veriqa.Core.ChannelAdapter` | Запрос подтверждения уходит в мессенджер |
| `veriqa.channel.inbound` | `Veriqa.Core.ChannelAdapter` | Событие канала приходит по вебхук-маршруту |
| `veriqa.transaction.complete` | `Veriqa.Core.TransactionEngine` | Транзакция завершается по данным канала: подтвердить, разрешить личность, завершить |

| Атрибут спана | Смысл |
| - | - |
| `veriqa.transaction.id` | Идентификатор транзакции, над которой работает спан |
| `veriqa.transaction.type` | Тип транзакции — на `veriqa.transaction.create` |
| `veriqa.transaction.trace_id` | Идентификатор трассы, создавшей транзакцию, — на `veriqa.channel.inbound` |
| `channel_type` | Тип канала — на канальных спанах |
| `error_code` | Код ошибки сбоя — на спане со статусом `Error` |

**Вход — это две трассы, а не одна.** Пользователь отвечает через произвольное время после запроса,
а мессенджер контекст трассы не передаёт, поэтому входящее событие открывает собственную трассу.
Сшиваются они атрибутом: `veriqa.transaction.trace_id` на входящем спане хранит идентификатор
трассы, создавшей транзакцию. Чтобы перейти от ответа к запросу входа, ищите в своём бэкенде по
этому значению.

* Для входа сохраняется идентификатор трассы запроса `/connect/authorize`. Если ваше приложение
  передаёт `traceparent`, это трасса вашего приложения.
* Для серверного подтверждения это `correlation_id`, переданный вами в запросе.
* То же значение пишется в поле `CorrelationId` записи аудита, так что журнал аудита и трассы
  соединяются им же.
* Хост без трассировки для входа значения не сохраняет — идентификатор взамен не выдумывается, — и
  входящий спан остаётся без атрибута.

<Note>
  **Как читать статус спана.** Спан помечается `Error` при сбое, который заканчивается внутри
  покрываемой им операции; причина — в `error_code`. Незаданный статус значит «здесь сбой не
  закончился», а не «операция прошла успешно»: исход транзакции — в метриках и журнале аудита.
</Note>

В режиме `Polling` входящего HTTP-запроса нет, и спана `veriqa.channel.inbound` тоже; спан
`veriqa.transaction.complete` при этом открывается.

## Логи

В self-hosted образе логи включаются тем же переключателем, что и остальные сигналы: при заданной
`OTEL_EXPORTER_OTLP_ENDPOINT` они экспортируются по OTLP, с отформатированным сообщением и
областями (scopes) логирования. Записи, сделанные внутри спана, несут идентификаторы его трассы и
спана, так что строка лога ведёт к своей трассе.

Что пишется и не пишется в логи и на каком уровне — в разделе
[Безопасность и обработка данных](/docs/ru/guides/data-handling#логи).

## Health-эндпоинты

Это эндпоинты self-hosted образа.

| Эндпоинт | Что отвечает |
| - | - |
| `/health/live` | Liveness — процесс поднят |
| `/health/ready` | Readiness — настроенные зависимости отвечают. Проба для вашего оркестратора |
| `/health/channels` | Состояние по каждому каналу. Всегда `200` — читайте тело, а не код статуса |
| `/health` | Все зарегистрированные проверки разом |
| `/alive` | Только проверка самого процесса |

Каналы намеренно не входят в readiness: недоступный мессенджер не должен выводить рабочую реплику
из ротации. Что `/health/channels` сообщает и чего не сообщает — в
[quickstart self-hosted](/docs/ru/quickstart/self-hosted#8-проверьте).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.