Использовать будем 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 лежат вот здесь

Если кому-то не нравятся бинарники, то вот подробная документация по установке 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<time>.*?)\] \[(?P<tenantid>.*?)\] \[(?P<severity>.*?)\] \[(?P<module>.*?)\] \[(?P<msg>.*?)\]

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

[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<time>.*?)\] \[(?P<tenantid>.*?)\] \[(?P<severity>.*?)\] \[(?P<module>.*?)\] \[(?P<msg>.*?)\]
    - labels:
        tenantid:
        severity:
        module:
    - timestamp:
        format: 02.01.2006 15:04:05
        source: time
        location: Asia/Krasnoyarsk
    - labeldrop:
        - filename
        - time
    - template:
        source: msgData
        template: '{{ .msg }}'
    - output:
        source: msgData

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

Настройка 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

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

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

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

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

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

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

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

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

Сначала нужно создать дашборд, в меню Home Dashboards жмем на кнопку New, в выпадающем списке выбираем New dashboard.

Дальше жмем на кнопку Add visualization.

Выбираем созданный на предыдущем шаге Loki datasource.

В правом верхнем углу меняем тип визуализации на Logs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Цвета для конкретных значений можно выбрать кликнув по тонкой цветной полоске
на легенде

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

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

Заключение

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

Комментарии (4)


  1. DonAlPAtino
    17.11.2023 11:28

    А это нормально что Promtail за localhost в google DNS пошел? Вроде у меня больше никто так не делает и все корректно настроено.


    1. piton_nsk Автор
      17.11.2023 11:28

      Вообще говоря это странно. Чтобы именно промтейле ходил в гугль. Как вы его вычислили, могу у себя проверить.


      1. DonAlPAtino
        17.11.2023 11:28

        в promtail-local-config.yaml

        clients:

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

        прописал и получил "error DNS resolve 8.8.8.8 53".

        Очень удивился, погуглил и поставил 127.0.0.1:3100. Все заработало


  1. DonAlPAtino
    17.11.2023 11:28

    Небольшой лог на 15 тыс строк с внутреннего портала. 7 лейблов.

    error="server returned HTTP status 429 Too Many Requests (429): Maximum active stream limit exceeded, reduce the number of active streams (reduce labels or reduce label values), or contact your Loki administrator to see if the limit can be increased, user: 'fake'"

    чего-то как-то быстро оно сдохло :-(