Делаем дашборд для логов используя Promtail Loki Grafana

Использовать будем Grafana версии 10.2, Promtail и Loki версии 2.9.2. Если кто-то вообще ничего не знает про используемый стек, посмотрите краткое описание вот здесь. Работать все будет под Windows, для Linux изменения должны быть чисто косметические. Статья написана для быстрого старта, поэтому здесь не будет разбираться быстродействие, настройка прав доступа, разнообразие визуализаций Grafana и прочее.

Подготовка

Скачиваем бинарники Grafana вот здесь. Если выбрана Enterpise лицензия, то можно вместо бинарников получить ошибку Sorry, our service is currently not available in your region. В этом случае выбирайте OSS.

Бинарники Loki и Promtail лежат вот здесь

-wquoscnvligbiu93glmpgbzsdk.png

Если кому-то не нравятся бинарники, то вот подробная документация по установке Promtail и Loki. Дефолтный конфиг версии 2.9.2 для Promtail здесь, а для Loki тут. Если нужна другая версия, меняйте в ссылках v2.9.2 на нужную.

Логи будут такого формата

[dateTime] [tenantId] [severity] [module] [message]

Пример записи в логе

[01.11.2023 17:07:59] [node001] [INFO] [Export.Csv] [rsvp_flow_stateMachine: entering]

Обратите особое внимание на свой формат даты и времени. Часовой пояс может быть явно указан в логах, можно настроить часовой пояс через конфиг promtail. Здесь он будет в конфиге. В разных часовых поясах легко запутаться, особенно если логи собираются на разных серверах и вдвойне легко запутаться если все происходит в облаке.

Для тестирования я себе делал генератор логов. Выдает он более-менее осмысленный контент нужного мне формата и нужного мне количества. У кого нет готовых логов для тестов, желательно заранее сделать файлик в котором будет хотя бы сотня записей. А для настройки Promtail достаточно вообще одной строки.

Настройка Promtail

У Promtail есть два параметра, которые сильно помогут в настройке

  • --dry-run данные не отправляются в Loki, также логфайл будет читаться с одного и того же места при каждом запуске. Можно не добавлять новые записи при каждом изменении конфига и запуске Promtail, достаточно одной строки.

  • --inspect в консоль выводится отладочная информация

Для настройки Promtail добавляем в логфайл одну запись и запускаем Promtail. Конфиг меняем с каждым запуском для тестирования изменений.

Выглядит это вот так

promtail-windows-amd64 --dry-run --inspect --config.file=promtail-local-config.yaml
[01.11.2023 17:07:59] [node001] [INFO] [Export.Csv] [rsvp_flow_stateMachine: entering state SESSIONED]
[inspect: regex stage]:
{stages.Entry}.Extracted["module"]:
        +: Export.Csv
{stages.Entry}.Extracted["msg"]:
        +: rsvp_flow_stateMachine: entering state SESSIONED
{stages.Entry}.Extracted["severity"]:
        +: INFO
{stages.Entry}.Extracted["tenantid"]:
        +: node001
{stages.Entry}.Extracted["time"]:
        +: 01.11.2023 17:07:59
[inspect: labels stage]:
{stages.Entry}.Entry.Labels:
        -: {filename="c:\\LogManagement\\logs\\test.log", job="logs"}
        +: {filename="c:\\LogManagement\\logs\\test.log", job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}
[inspect: timestamp stage]:
{stages.Entry}.Entry.Entry.Timestamp:
        -: 2023-11-03 16:32:11.2903454 +0700 +07
        +: 2023-11-01 17:07:59 +0700 +07
[inspect: labeldrop stage]:
{stages.Entry}.Entry.Labels:
        -: {filename="c:\\LogManagement\\logs\\test.log", job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}
        +: {job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}
[inspect: template stage]:
{stages.Entry}.Extracted["msgData"]:
        +: rsvp_flow_stateMachine: entering state SESSIONED
[inspect: output stage]:
{stages.Entry}.Entry.Entry.Line:
        -: [01.11.2023 17:07:59] [node001] [INFO] [Export.Csv] [rsvp_flow_stateMachine: entering state SESSIONED]
        +: rsvp_flow_stateMachine: entering state SESSIONED
2023-11-01T17:07:59+0700{job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}  rsvp_flow_stateMachine: entering state SESSIONED

Теперь будет настройка конфига по частям. Если удобнее смотреть все сразу, то дальше весь конфиг целиком.
Для настройки берем дефолтный конфиг, вот эту часть оставляем как есть

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

Дальше меняем path и job
job logs метка для имени конфигурации (конфигураций может быть несколько)
path путь к файлу (или файлам) логов. Если у вас настроен log file rotation, то хорошо проверьте шаблон, чтобы promtail точно находил новые файлы.

scrape_configs:
- job_name: system
  static_configs:
  - targets:
      - localhost
    labels:
      job: logs
      __path__: c:\LogManagement\logs\test.log

Дальше настраиваем непосредственно разбор логов. Разбор происходит по определенным стадиям/этапам, подробности в документации. Для разбора строки используем регулярку с именованными группами. Имя группы будет использовано на следующих этапах. Для json или logfmt есть стандартные настройки, изобретать регулярки для разбора json не нужно.

  pipeline_stages:
    - regex:
        expression:
          \[(?P

Результат разбора

[01.11.2023 17:07:59] [node001] [INFO] [Export.Csv] [rsvp_flow_stateMachine: entering state SESSIONED]
[inspect: regex stage]:
{stages.Entry}.Extracted["module"]:
        +: Export.Csv
{stages.Entry}.Extracted["msg"]:
        +: rsvp_flow_stateMachine: entering state SESSIONED
{stages.Entry}.Extracted["severity"]:
        +: INFO
{stages.Entry}.Extracted["tenantid"]:
        +: node001
{stages.Entry}.Extracted["time"]:
        +: 01.11.2023 17:07:59

Дальше стадия меток, тут мы указываем какие метки будут у записи.

    - labels:
        tenantid:
        severity:
        module:

Результат ниже, обратите внимание на то, какие были метки до, и какие стали после.

[inspect: labels stage]:
{stages.Entry}.Entry.Labels:
        -: {filename="c:\\LogManagement\\logs\\test.log", job="logs"}
        +: {filename="c:\\LogManagement\\logs\\test.log", job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}

Теперь очень важная часть — установка времени для записи. В моем примере в логах часовой пояс явно не указан, поэтому часовой пояс выставлен через конфиг. Если у вас не так, location выкидываете, format меняете под свой. В Promtail используется Go формат для времени и дат. Кто с этим форматом незнаком, вот шпаргалка. Коды таймзон для конфига можно посмотреть здесь.

    - timestamp:
        format: 02.01.2006 15:04:05
        source: time
        location: Asia/Krasnoyarsk

Результат работы этапа ниже, обратите внимание что дата поменялась на 2 дня. В файле логов дата 1 ноября, обработка происходит 3 ноября. По умолчанию в записи будет проставлено время обработки лога. Тщательно протестируйте этот этап, особенно если логи создаются в облаке.

[inspect: timestamp stage]:
{stages.Entry}.Entry.Entry.Timestamp:
        -: 2023-11-03 16:32:11.2903454 +0700 +07
        +: 2023-11-01 17:07:59 +0700 +07

Дальше выкидываем лишние метки, filename нам не нужен, time тоже не нужен, потому что со временем записи в логе мы разобрались на предыдущей стадии.

    - labeldrop:
        - filename
        - time

Результат работы этапа

[inspect: labeldrop stage]:
{stages.Entry}.Entry.Labels:
        -: {filename="c:\\LogManagement\\logs\\test.log", job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}
        +: {job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}

Теперь создадим новое поле msgData используя шаблон, данные для нового поля будут из старого поля msg. Выглядит немного кривовато, но это сделано специально. Для того чтобы показать как можно менять строковое содержания логов еще при работе Promtail, до того как они попадут в Loki. Здесь из строки выкидываются tenant, severity, node и time, т.к. эти данные все равно будут в метках. Если надо оставить строку как есть, тогда этот и следующий этап можно просто убрать из конфига.

    - template:
        source: msgData
        template: '{{ .msg }}'

Результат работы этапа — строка лога

[inspect: template stage]:
{stages.Entry}.Extracted["msgData"]:
        +: rsvp_flow_stateMachine: entering state SESSIONED

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

    - output:
        source: msgData

Итоговая запись — строка и метки

[inspect: output stage]:
{stages.Entry}.Entry.Entry.Line:
        -: [01.11.2023 17:07:59] [node001] [INFO] [Export.Csv] [rsvp_flow_stateMachine: entering state SESSIONED]
        +: rsvp_flow_stateMachine: entering state SESSIONED
2023-11-01T17:07:59+0700{job="logs", module="Export.Csv", severity="INFO", tenantid="node001"}  rsvp_flow_stateMachine: entering state SESSIONED

Конфиг целиком

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

scrape_configs:
- job_name: system
  static_configs:
  - targets:
      - localhost
    labels:
      job: logs
      __path__: c:\LogManagement\logs\test.log
  pipeline_stages:
    - regex:
        expression:
          \[(?P

У Promtail есть свой простенький веб-клиент, порт по умолчанию 9080, можно использовать для проверки работоспособности и администрирования, выглядит клиент вот так

tsgjh37il-kzgzqyij6q6mrrbmw.png

Настройка Loki

Здесь все просто, используете дефолтный конфигурационный файл и все работает. По умолчанию Loki отсылает какую-то статистику на свои сервера. Чтобы это отключить раскомментируйте эти строчки

#analytics:
#  reporting_enabled: false

Для проверки работоспособности Loki зайдите в браузере (или в любом веб клиенте) по ссылке http://localhost:3100/ready. В ответе должно быть ready, еще может быть такой ответ Ingester not ready: waiting for 15s after being ready, подождите указанные 15 секунд и попробуйте еще раз. У Loki есть свое API, cправка тут. Из полезного на начальной стадии может быть ендпойнт для метрик http://localhost:3100/metrics.

Настройка Grafana

Запускаем файл grafana-10.2.0\bin\grafana-server.exe и заходим на http://localhost:3000. Логин пароль при первом запуске admin/admin, Grafana сразу предложит его сменить. Loki должен быть запущен и в нем должны быть данные.

Добавляем Loki Datasource

В меню Home Connections Data sources жмем на кнопку Add new data source

0eh7fddg4mbmsktwcvgzlthrx2m.png

Там выбираем Loki

uieofuhp485b4ddr2ahhof26bwu.png

В поле Connection вводим http://127.0.0.1:3100

se3wpjzlxqf1dt-ksfc7j-3kbuk.png

Внизу формы жмем на Save & Test и дожидаемся подтверждения успешно установленного соединения

uxwpugvizkqvj7gt6jdtmpxb1ae.png

В правом верхнем углу жмем на кнопку explore data

65zska3oozryn0qoxf3h-vpdaaa.png

Откроется форма для создания и редактирования запросов

ps29cbn9oen3lyl8ja5utscty9s.png

Кликнув по кнопке Label browser можно посмотреть какие метки и какие у них значения есть в базе, составить и выполнить запрос

kqlzh7fws1z54vkelkilmpjqvdo.png

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

z0rnc2jf9qf3pq1xwklx15xxpdk.png

Создаем панель Raw Logs

Сначала нужно создать дашборд, в меню Home Dashboards жмем на кнопку New, в выпадающем списке выбираем New dashboard
Дальше жмем на кнопку Add visualization
Выбираем созданный на предыдущем шаге Loki datasource
В правом верхнем углу меняем тип визуализации на Logs

ofijffyrbfzht3q-32naawpoh7i.png

Меняем Panel title на Raw logs и включаем галочки Time и Enable log details

b5ezuy1lxt70wsbez19rxl3llhw.png

Жмем на кнопку Apply

sccqtrpqgsoorh7brz6bdqkctai.png

Заходим в свойства дашборда

vfjro-rvymugb_of3fonvaj0vrq.png

Здесь заходим на вкладку Variables и жмем на кнопку Add variable, имя переменной tenantid, остальное как на картинке ниже

rtcnhsynjr2x6bcwpvws-zh5n8g.png

Создаем еще одну переменную, все тоже самое, только для nodeid

Создаем третью переменную с названием filter и типом Text box

wiukvh8l9aaj8qlvdsourgtak7w.png

Панель теперь выглядит так. Жмем edit чтобы поменять запрос

j-8kmyvishetnb1x6_efvknshqi.png

Запрос будет такой. Обратите внимание на line_format, так можно менять отображение записей лога.

{job="logs", tenantid=~"$tenantid", severity=~"$severity"} |= "$filter" | line_format "{{.tenantid}} {{.module}} {{.severity}} {{__line__}}"

В итоге выглядеть все будет как на картинке ниже, можно выбирать tenant, severity и отфильтровать по содержимому лога.

pr_xvjgltfpiqkcl9cqd9skvjgg.png

Создаем панель Pie Chart

Жмем на кнопку Add выбираем Visualization

hmk5gffig8ayqy_-ca04fxoafiw.png

Выставляем свойства как указано на картинке ниже

2yynj_ko6taq_5cxg23ljxe3lzo.png

Запрос будет такой

sum by(severity) (count_over_time({job="logs"} [$__range]))

Обратите внимание что в запросе нет переменных, которые есть в запросе для панели Raw Logs. Поэтому в чарте будут использоваться все данные за выбранный промежуток времени, фильтрации не будет. При желании можно в запрос добавить переменные и тогда данные в чарте и в панели Raw Logs будут одинаковые.
Цвета для конкретных значений можно выбрать кликнув по тонкой цветной полоске
на легенде

ke82x5bamxpjarklegi-slilxoe.png

Или добавив нужные значение на вкладке Overrides в свойствах чарта

av_xjqsu3ja3wmdhclievkfc45u.png

В итоге наш чарт будет выглядеть вот так

p0t5j_rijkuhyj93cwtxjd2t_0c.png

Заключение

Grafana классный инструмент для визуализации данных, Loki и Promtail дают возможность достаточно просто настроить сбор и хранение логов. Сочетание этих инструментов дает очень хороший результат при небольших усилиях. Надеюсь статья будет полезной.

© Habrahabr.ru