Как читать логи в Loki
Шпаргалка по LogQL: где смотреть, как фильтровать, как коррелировать с
трейсами. Полный reference по формату логов — в
../conventions/logging.
Содержание
- Где логи
- Основы LogQL
- Структурированный поиск по JSON
- Rate и агрегация
- Временные диапазоны
- Типовые запросы
- Correlation с traces
- Локальные логи (dev)
- Troubleshooting
- Anti-patterns
- Связанные разделы
Где логи
Все prod-логи централизованы в Loki. UI — Grafana → Explore → datasource Loki.
Все сервисы пишут JSON в stdout, контейнерный log driver кладёт поток
в Loki. Формат записи зафиксирован в
../conventions/logging: service,
request_id, correlation_id, trace_id, user_id, route, level
и прочее — обязательные структурные поля.
Основы LogQL
Базовая форма: {<label-selector>} <pipeline>.
Выбор потока по labels
{service="review"}Labels — это метаданные Loki (service, pod, namespace), не поля JSON-payload’а. Индексируются заранее, поэтому фильтруй сначала по ним.
Текстовый фильтр
{service="review"} |= "error" # substring
{service="review"} != "health" # отрицание
{service="review"} |~ "(?i)panic" # regex, case-insensitive
{service="review"} !~ "^debug" # отрицание regexКомбинации
{service="review", namespace="prod"} |= "outbox" != "healthz"Структурированный поиск по JSON
Каждая запись — JSON; распарсить можно через | json:
{service="review"} | jsonДальше — фильтры по полям:
{service="review"} | json | user_id="42"
{service="review"} | json | status_code >= 500
{service="review"} | json | duration_ms > 1000
{service="review"} | json | level="ERROR"Комбинируй:
{service="review"}
| json
| level="ERROR"
| route="/v1/reviews"
| user_id="42"Line format для удобочитаемости
{service="review"}
| json
| level="ERROR"
| line_format "{{.time}} {{.request_id}} {{.err}}"Rate и агрегация
Count за период
count_over_time({service="review"} |= "level=ERROR" [5m])Rate per second
rate({service="review"} |= "level=ERROR" [5m])Группировка
sum by (status_code) (
rate({service="review"} | json [5m])
)Топ-N по error message
topk(10,
sum by (msg) (
count_over_time({service="review"} | json | level="ERROR" [15m])
)
)Временные диапазоны
- UI по-умолчанию — последний час. Меняй через селектор в правом верхнем углу Grafana.
- URL-параметры —
&from=1713830400000&to=1713834000000(ms epoch). Удобно шарить ссылку с инцидентом. - Long-term (> 30 дней) — может быть недоступно из-за Loki retention. Для исторического анализа — экспорт или отдельный storage.
Типовые запросы
Ошибки конкретного сервиса за 15 минут
{service="review"} | json | level="ERROR"Все логи конкретного user за час
{service=~".+"} | json | user_id="42"Полезно при обращении в поддержку «у пользователя 42 что-то не работает» — собираешь полную трассу его действий по всем сервисам.
Все логи одного trace
{service=~".+"} | json | trace_id="4bf92f3577b34da6a3ce929d0e0e4736"Переход с trace на логи — основной способ распутать distributed
инцидент. См. ./read-traces.
Latency spike debug
{service="review"}
| json
| duration_ms > 500
| line_format "{{.time}} {{.route}} {{.duration_ms}}ms {{.request_id}}"Outbox lag в конкретный момент
{service="review"}
| json
| msg="outbox forwarder tick"
| unacked != "0"Kafka consumer ошибки
{service="notification"}
| json
| msg="kafka consume"
| level=~"WARN|ERROR"Все HTTP 5xx за час
{service=~".+"}
| json
| status_code >= 500
| line_format "{{.service}} {{.route}} {{.status_code}} {{.request_id}}"Correlation с traces
Loki ↔ Tempo интеграция настроена в Grafana datasource’ах.
- Из Tempo в Loki. Открыл trace → каждый span имеет кнопку
Logs for this span. Под капотом запрос вида
{service="<span.service>"} | json | trace_id="<trace>". - Из Loki в Tempo. В JSON-записи, где виден
trace_id, в раскрытом представлении кнопка View trace открывает trace в Tempo.
Это базовый workflow отладки распределённого запроса:
- Grafana dashboard показывает аномалию (p99 latency spike).
- Exemplar на графике → прыжок в Tempo с конкретным trace.
- Из trace → в Loki по
trace_id→ полный контекст каждого span’а.
Локальные логи (dev)
Когда Loki не доступен (локальная разработка, standalone-запуск):
docker compose logs -f review | jq 'select(.level=="ERROR")'
docker compose logs -f review | jq 'select(.user_id==42)'
docker compose logs -f review | jq 'select(.trace_id=="abc123")'Для plain-text в dev используется SERVICE_LOG_FORMAT=text, если сервис
поддерживает (см. ../conventions/logging).
В prod — только JSON, jq не нужен.
Troubleshooting
«Нет данных за N минут»
- Проверь, что селектор labels соответствует живым потокам:
{service=~".+"}без других фильтров за те же 5 минут. - Если и этого нет — проблема в ingester Loki, эскалируй SRE.
Slow queries
- Сузь
time rangeдо минимально нужного. - Добавь label-фильтр (
namespace,service) — Loki не ищет в потоках, которые ты не выбрал. - Вынеси
| jsonпосле всех текстовых фильтров:|=и!=отрабатывают до парсинга и дёшевы.
«No logs found»
- Проверь правильность имени сервиса в label:
{service="review"}, не{service="review-service"}. Значение label приходит изnewLogger(...)With("service", "review")— см.../conventions/logging. - Проверь time range. Инцидент был час назад, а ты смотришь «последние 15 минут» — данных нет.
Только часть полей доступна после | json
Loki по умолчанию извлекает «плоский» JSON. Вложенные объекты надо доставать явно:
{service="review"} | json user="user_id"Anti-patterns
{service=~".+"}на всё без time-window. Запрос идёт по всему кластеру на длительный период — timeout’ится и нагружает ingester.- Полнотекстовый regex без label-selector.
|~ "foo"на всём — то же самое. - Поиск по
msgsubstring вместо label. Если одна и та же строка пишется в десятке мест — фильтруй по дополнительному JSON-полю (event_type,route), а не по кускуmsg. - Зависимость от поля, которого нет.
| json | err != ""падает, если в записи поляerrнет. Используй| err=""наоборот — LogQL нормально трактует отсутствие. - Логика через логи. Если алерт рассчитывается LogQL-ом на каждое срабатывание — это гораздо дороже, чем counter-метрика. Метрики — в Prometheus, логи — для контекста.
Связанные разделы
../conventions/logging— формат JSON-записи, обязательные поля, PII-маскирование.../conventions/observability— как связаны logs/metrics/traces../read-traces— Tempo для distributed tracing.