Логи
Раздел телеметрии для структурированных логов приложения: приём через API (описан ниже) и экран просмотра/поиска в интерфейсе.
Просмотр и поиск
Экран логов проекта — /projects/{id}/logs (пункт «Логи» в навигации
проекта). Список — самые новые записи сверху; клик по строке раскрывает
полное тело, таблицу атрибутов (log_attributes и resource_attrs отдельно)
и trace_id/span_id, если запись пришла с ними.
Фильтры
Форма фильтров над списком — без обязательного JavaScript (обычная GET-форма, «Применить» пересобирает страницу):
- Период — общий контрол окна времени продукта
(пресеты 1ч/24ч/7д/30д + произвольный диапазон). Для логов окно
дополнительно обрезается сроком хранения (
GOTCHA_LOG_RETENTION_DAYS, по умолчанию 14 дней — см. Конфигурацию): выбранный пресет шире фактического TTL даёт укороченное окно, запрос за пределы хранения всё равно вернул бы пусто. Экран показывает это явно — подпись «окно урезано до срока хранения логов (N дней)» рядом с фильтрами, а не молча более короткий список. - Уровень — мультивыбор из шести канонических severity (
trace…fatal, см. канонизацию ниже). - Сервис и Окружение — точное совпадение по значениям, которые попали
в запись при приёме (
service.name/deployment.environment.nameиз OTLP). - Поиск по тексту — подстрочный (регистронезависимый) поиск по телу
записи (
body).
Активные фильтры сохраняются в адресной строке — ссылкой на текущий срез можно поделиться или обновить страницу без потери состояния.
Фасеты
Сайдбар справа от списка показывает фасеты — топ значений с числом записей в текущем окне и с учётом остальных активных фильтров:
- Уровень, сервис, окружение — те же три измерения, что и в форме фильтров; клик по значению добавляет его в фильтр (или снимает, если оно уже выбрано). У фасета «Уровень» свой фильтр в подсчёт не входит — он всегда показывает распределение по всем уровням, даже когда один-два уже выбраны (иначе выбранный уровень тут же «съедал» бы весь фасет).
- Атрибуты — ключи, автоматически обнаруженные в
log_attributesзаписей текущего окна (топ по частоте), каждый со счётчиком. Клик по ключу раскрывает его топ-10 значений (тоже со счётчиками) — сервер считает их «лениво», только для раскрытого ключа, а не для всех сразу. Клик по значению добавляет точечный фильтр по этому атрибуту (см. ниже).
Если фасет не успевает посчитаться в отведённое время (очень широкое окно и объём данных), вместо значений он показывает пометку «слишком много данных» — сузьте окно или добавьте фильтр. Это не ошибка всей страницы: список и остальные фасеты остаются рабочими.
Автокомплит ключей атрибутов
Поле поиска атрибута в сайдбаре подсказывает ключи по мере ввода (typeahead):
начните печатать префикс (например, http.) — предложатся совпавшие ключи
(http.method, http.status_code, …) с частотой. Ищет в том же окне
времени, что и текущий фильтр списка (пресет/произвольный диапазон выше), а
не в отдельном фиксированном окне — подсказки совпадают с тем, что видно в
сайдбаре и списке. Это JS-улучшение поверх сайдбар-фасетов «Атрибуты», а не
отдельная форма: без JavaScript само поле неактивно (никуда не отправляется),
но ключи всё равно доступны — списком с частотами прямо в сайдбаре, кликом
раскрывающим топ значений (см. выше).
Точечные фильтры по атрибутам
Кроме клика по фасету, срез по конкретному атрибуту можно задать вручную
через query-параметр attr, повторяемый для нескольких условий:
/projects/{id}/logs?attr=http.method:GET&attr=http.status_code:500
Значение — key:value, разделитель первого : (само значение может
содержать двоеточие, например URL: attr=http.url:http://example.com/x).
Чтобы отфильтровать по атрибуту ресурса (resource_attrs, а не
log_attributes) — префикс res:: attr=res:host.name:web-01. Несколько
attr сужают выборку (логическое И). Поддерживается только точное
совпадение — регулярные выражения и «не равно» вне MVP.
Гистограмма объёма
Над списком — гистограмма количества записей по времени, разбитая по severity (тот же стек-стиль, что и у остальных графиков продукта). Ширина корзины подбирается под размах выбранного окна. Если за текущий фильтр нет данных, гистограмма скрывается вместо пустого графика.
Пагинация
Список показывает порцию свежих записей; кнопка «Показать старее» подгружает следующую такую же порцию курсором по времени — счётчика «всего найдено» и глубокой постраничной навигации нет (на объёме логов offset-пагинация с подсчётом общего числа была бы дорогим полным сканом). Собрать точную статистику по всему периоду проще через фасеты и гистограмму, чем листанием списка до конца.
Склейка с ошибками, трейсами и хостами
Экран логов принимает фильтр по trace_id через query-параметр ?trace_id=…
— в списке фильтров он показывается отдельным снимаемым чипом (крестик рядом
с укороченным ID снимает фильтр, не трогая остальные). Задать его вручную
можно и напрямую в адресной строке, но обычно на экран логов попадают уже с
готовым срезом по одной из трёх ссылок в остальном интерфейсе:
- «Логи вокруг события» — на странице ошибки, у выбранного события.
Если у события есть
trace_id, ссылка ведёт на логи с этим trace_id внутри окна[время события ± 5 мин]; еслиtrace_idнет — сужает тот же временной срез по environment события вместо точного trace-фильтра. - «Логи этого трейса» — на waterfall трейса (
/traces/{id}). Точный фильтр поtrace_idплюс временное окно самого трейса (от начала до конца- запас в 1 секунду), чтобы приём не сканировал лишние партиции.
- «Логи хоста» — на карточке хоста. Не про
trace_id: это точечный фильтр по resource-атрибуту (?attr=res:host.name:<имя>), см. «Точечные фильтры по атрибутам» выше. Работает, только если сам источник логов кладётhost.nameв resource-атрибуты записи — у собственногоgotcha-agent(см. Хосты) логов нет вообще, он шлёт только метрики, поэтому эта ссылка покажет пустой список логов для хостов, подключённых черезgotcha-agent. Атрибут появится, только если тот же хост дополнительно шлёт логи через OTel-коллектор сresource.attributes.host.name, выставленным вручную в его конфиге.
Как отправить лог
Принимаются два независимых формата на разных путях одного хоста (см. DSN проекта) — выбирайте тот, что проще для вашего стека.
Аутентификация
Как и /v1/metrics, оба эндпойнта приёма логов авторизуются публичным ключом
проекта в заголовке:
Authorization: Bearer <PUBLIC_KEY>
<PUBLIC_KEY> — часть DSN проекта между https:// и @ (см. страницу
проекта «Подключение» и SDK и интеграции). Неверный или
отсутствующий ключ — ответ 401.
OTLP: POST /v1/logs
Для клиентов с OTel SDK/коллектором — тот же протокол, что у метрик и трасс. Поддерживаются обе стандартные кодировки OTLP:
Content-Type | Формат тела |
|---|---|
application/x-protobuf (или application/protobuf) | Бинарный protobuf — то, что шлёт экспортёр OTel по умолчанию |
application/json | OTLP/JSON — тот же протокол в JSON |
Пример OTLP/JSON без SDK:
curl -X POST https://gotcha.example.com/v1/logs \
-H "Authorization: Bearer a1b2c3d4e5f6" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [{
"resource": {
"attributes": [
{"key": "service.name", "value": {"stringValue": "my-php-app"}},
{"key": "deployment.environment.name", "value": {"stringValue": "production"}}
]
},
"scopeLogs": [{
"logRecords": [{
"timeUnixNano": "1700000000000000000",
"severityNumber": 17,
"severityText": "ERROR",
"body": {"stringValue": "payment failed: gateway timeout"},
"attributes": [{"key": "order_id", "value": {"stringValue": "42"}}],
"traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
"spanId": "051581bf3cb55c13"
}]
}]
}]
}'
Ответ при успехе — пустой 200 OK (стандартный пустой OTLP-конверт).
service.name/deployment.environment.name (или устаревший
deployment.environment) из ресурсных атрибутов идут в поля «сервис» и
«окружение» записи, как и у метрик; traceId/spanId сохраняются как есть
(hex) — задел под склейку лог↔трейс в будущей версии.
Текст записи (body) проходит тот же безусловный скраб URL, что и сообщения
ошибок: query-токены и basic-auth в URL внутри текста вычищаются всегда,
независимо от GOTCHA_SCRUB_FREETEXT (см. Приватность) —
приватность по умолчанию, как у остальной телеметрии.
Для регулярной отправки удобнее настроить экспортёр логов вашего OTel SDK переменными окружения, аналогично метрикам:
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://gotcha.example.com/v1/logs
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_LOGS_HEADERS=Authorization=Bearer%20a1b2c3d4e5f6
NDJSON: POST /logs
Более простой путь для источников без OTel-экспортёра — произвольный скрипт,
агент сбора логов, ручной curl. Тело — newline-delimited JSON: одна запись
на строку, без обёртки массивом.
Схема строки:
| Поле | Обязательность | Описание |
|---|---|---|
message | обязательно | Текст записи. Пустая или отсутствующая строка — запись строки пропускается целиком (не роняет остальной батч). |
level | опционально | Текстовый уровень (info, warn, err, critical и т. п. — см. канонизацию ниже). Пусто или нераспознано → info. |
timestamp | опционально | RFC3339-строка ("2026-08-18T12:00:00Z") либо unix-время в секундах числом (дробная часть допускается, 1755518400.5). Отсутствует, пусто, не разобрано или не позже эпохи — берётся время приёма сервером. |
attributes | опционально | Произвольный JSON-объект: значения-строки/числа/bool копируются как есть, вложенные объекты/массивы сериализуются обратно в JSON-строку. |
trace_id | опционально | Строка (без проверки формата, в отличие от OTLP), капается до 64 символов — длиннее обрезается, как и у прочих полей. |
span_id | опционально | То же самое. |
NDJSON-записи не несут ресурсных атрибутов — поля «сервис» и «окружение» у них не заполняются (это может измениться в будущей версии); если нужна эта привязка уже сейчас, используйте OTLP.
Пример:
curl -X POST https://gotcha.example.com/logs \
-H "Authorization: Bearer a1b2c3d4e5f6" \
--data-binary $'{"message":"payment failed: gateway timeout","level":"error","attributes":{"order_id":42}}\n{"message":"retrying in 5s","level":"info"}\n'
Ответ при успехе — 200 OK с телом {"accepted": N}, где N — число
записей, реально принятых в квоту и сохранённых (может быть меньше числа
строк в теле, если часть отклонена по квоте — см. ниже). Строка, которая не
разбирается как JSON, или пустой message — строка молча пропускается и не
входит в N, но не роняет остальной батч.
Что канонизируется в severity
UI и правила алертинга (когда появятся) работают с единым каноном из шести уровней, к которому приводится любой источник:
trace, debug, info, warn, error, fatal
Из OTLP SeverityNumber (1–24, спецификация OTel): 1–4 → trace, 5–8 →
debug, 9–12 → info, 13–16 → warn, 17–20 → error, 21–24 → fatal.
Число вне диапазона (0, отрицательное, >24) — не ошибка формата, запись не
теряется, а получает нейтральный info.
Из текста (NDJSON level, а также OTLP SeverityText — но только как
запасной путь, если SeverityNumber не заполнен): trace; debug; info;
warn/warning; error/err; fatal/critical (регистр не важен).
Числовая строка ("17") трактуется как SeverityNumber. Пустой или
нераспознанный текст — тоже info.
Исходные severity_number/severity_text сохраняются рядом с канонической
severity — для отладки и аудита, не только сам канон.
Лимиты
- До 10000 записей в одном запросе (и OTLP, и NDJSON) — лишние в пределах запроса отбрасываются, батч не падает целиком.
- До 64 КиБ на текст записи (
body/message) — длиннее обрезается. - NDJSON: строка длиннее 256 КиБ отбрасывается без попытки разбора (и не считается ошибкой всего запроса).
- До 64 атрибутов на запись, ключ — до 64 символов, значение — до 200; при превышении оставляются первые 64 по отсортированным ключам (детерминированно, не случайно).
- Общий размер тела запроса ограничен той же переменной, что и у остальной
телеметрии,
GOTCHA_MAX_EVENT_BYTES(см. Конфигурацию) — тело крупнее лимита отклоняется с413. - Превышение частоты запросов на DSN или исчерпание квоты —
429.
Окно приёма
Запись со временем старше 90 дней в прошлом или больше суток в будущем относительно момента приёма прижимается к границе этого окна (не отбрасывается совсем, в отличие от метрик — тело и уровень записи остаются полезны, даже если у клиента разъехались часы).
Хранение, квота и настройки
Логи хранятся в ClickHouse по общей политике инстанса, отдельной страницы
настроек на уровне проекта пока нет. Срок хранения задаётся оператором через
GOTCHA_LOG_RETENTION_DAYS (по умолчанию 14 дней — логи объёмнее событий,
поэтому дефолт короче; 0 — хранить бессрочно). Приём логов считается в
месячную квоту организации: лимит по умолчанию — GOTCHA_DEFAULT_LOG_QUOTA,
тонко настраивается в «Настройках организации → Использование и лимиты» (см.
Конфигурацию). При исчерпании квоты /v1/logs и
/logs отвечают 429, уже принятые записи не удаляются.