В комментариях к моему туториалу, рассказывающему о парсинге логов с помощью Fluent-bit, было приведено две альтернативы: Filebeat и Vector. Этот туториал рассказывает как организовать сбор и парсинг лог-сообщений при помощи Filebeat.


Цель туториала: Организовать сбор и парсинг лог-сообщений с помощью Filebeat.


Дисклеймер: Туториал не содержит готовых для продакшена решений, он написан с целью помочь тем кто только начинает разбираться с filebeat и для закрепления изученного материала автором. Так же в туториале не проводиться сравнение доставщиков логов. Сравнение можно найти здесь.


Кому данная тема интересна, прошу под кат:)


Тестовое приложение будем запускать с помощью docker-compose.


Общая информация


Filebeat — это легковесный доставщик лог-сообщений. Принцип его работы состоит в мониторинге и сборе лог-сообщений из лог-файлов и пересылки их в elasticsearch или logstash для индексирования.


Filebeat состоит из ключевых компонентов:


  • сборщики (harvesters) — отвечают за чтение лог-файлов и отправку лог-сообщений в заданный выходной интерфейс, на каждый лог-файл задается отдельный сборщик;
  • входные интерфейсы (inputs) — отвечают за поиск источников лог-сообщений и управление сборщиками.

Подробнее о принципе работы можно почитать в официальном руководстве.


Организация сбора лог-сообщений


Filebeat имеет множество входных интерфейсов для различных источников лог-сообщений. В рамках туториала предлагаю двигаться от настройки сбора вручную до автоматического поиска источников лог-сообщений в контейнерах. По моему мнению данный подход позволит глубже понять filebeat, к тому же я сам двигался тем же путем.


Нам необходим сервис, лог-сообщения которого будем пересылать на хранение.
В качестве такого сервиса возьмем простое приложение, написанное с помощью FastAPI, единственной целью которого является генерация лог-сообщений.


Сбор лог-сообщений с помощью volume


Для начала клонируем репозиторий. В нем находится тестовое приложение, конфигурационный файл Filebeat и docker-compose.yml.
Настройка сбора лог-сообщений с помощью volume состоит из следующих шагов:


  1. Настройка логгера приложения на запись лог-сообщений в файл:

    app/api/main.py

    logger.add(
        "./logs/file.log",
        format="app-log - {level} - {message}",
        rotation="500 MB"
    )
  2. Создание volume для хранения лог-файлов вне контейнеров:
    docker-compose.yml


    version: "3.8"
    
    services:
      app:
        ...
        volumes:
          # создаем volume, для хранения лог-файлов вне контейнера
          - app-logs:/logs
    
      log-shipper:
        ...
        volumes:
          # заменяем конфигурационный файл в контейнере
          - ./filebeat.docker.yml:/usr/share/filebeat/filebeat.yml:ro
          # подключаем volume с лог-файлами в контейнер
          - app-logs:/var/app/log
    
    volumes:
      app-logs:

  3. Определение входного и выходного интерфейсов filebeat:
    filebeat.docker.yml


    filebeat.inputs:
    - type: log
      # Определяем путь к лог-файлам
      paths:
        - /var/app/log/*.log
    
    # Пока будем выводить лог-сообщения в консоль
    output.console:
      pretty: true

    Запускаем тестовое приложение, генерируем лог-сообщения и получаем их в следующем формате:


    {
    "@timestamp": "2021-04-01T04:02:28.138Z",
    "@metadata": {
    "beat": "filebeat",
    "type": "_doc",
    "version": "7.12.0"
    },
    "ecs": {
    "version": "1.8.0"
    },
    "host": {
    "name": "aa9718a27eb9"
    },
    "message": "app-log - ERROR - [Item not found] - 1",
    "log": {
    "offset": 377,
    "file": {
      "path": "/var/app/log/file.log"
    }
    },
    "input": {
    "type": "log"
    },
    "agent": {
    "version": "7.12.0",
    "hostname": "aa9718a27eb9",
    "ephemeral_id": "df245ed5-bd04-4eca-8b89-bd0c61169283",
    "id": "35333344-c3cc-44bf-a4d6-3a7315c328eb",
    "name": "aa9718a27eb9",
    "type": "filebeat"
    }
    }


Сбор лог-сообщений с помощью входного интерфейса container


Сontainer позволяет осуществлять сбор лог-сообщений с лог-файлов контейнеров.
Настройка сбора лог-сообщений с помощью входного интерфейса container состоит из следующих шагов:


  1. Удаление настроек входного интерфейса log, добавленного на предыдущем этапе, из конфигурационного файла.
  2. Определение входного интерфейса container в конфигурационном файле:
    filebeat.docker.yml


    filebeat.inputs:
    - type: container
      # путь к лог-файлам контейнеров
      paths:
        - '/var/lib/docker/containers/*/*.log'
    
    # Пока будем выводить лог-сообщения в консоль
    output.console:
      pretty: true

  3. Отключаем volume app-logs из сервисов app и log-shipper и удаляем его, он нам больше не понадобиться.
  4. Подключаем к сервису log-shipper лог-файлы контейнеров и сокет докера:
    docker-compose.yml


    version: "3.8"
    
    services:
      app:
        ...
    
      log-shipper:
        ...
        volumes:
          # заменяем конфигурационный файл в контейнере
          - ./filebeat.docker.yml:/usr/share/filebeat/filebeat.yml:ro
          - /var/lib/docker/containers:/var/lib/docker/containers:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro

  5. Настройка логгера приложения на запись лог-сообщений в стандартный вывод:
    app/api/main.py
    logger.add(
        sys.stdout,
        format="app-log - {level} - {message}",
    )

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


Сбор лог-сообщений с помощью автообнаружения


При сборе лог-сообщений контейнеров, могут возникнуть трудности, так как контейнеры могут перезапускаться, удаляться и т.д. В связи с этим в filebeat есть автообнаружение контейнеров, с возможностью определения настроек сбора лог-сообщений для каждого обнаруженного контейнера. Механизм автообнаружения состоит из двух частей:


  • шаблона поиска контейнера;
  • конфигурации сбора лог-сообщений.

Подробнее можно почитать здесь.


Настройка состоит из следующих шагов:


  1. Удаление настроек входного интерфейса container, добавленного на предыдущем этапе, из конфигурационного файла.
  2. Определение настроек автообнаружение в конфигурационном файле:
    filebeat.docker.yml


    filebeat.autodiscover:
      providers:
        # искать docker контейнер
        - type: docker
          templates:
            - condition:
                contains:
                  # имя которого fastapi_app
                  docker.container.name: fastapi_app
              # определим конфигурацию сбора для этого контейнера
              config:
                - type: container
                  paths:
                    - /var/lib/docker/containers/${data.docker.container.id}/*.log
                  # исключим лог-сообщения asgi-сервера
                  exclude_lines: ["^INFO:"]
    
    # Пока будем выводить лог-сообщения в консоль
    output.console:
      pretty: true


Вот и все. Теперь filebeat будет собирать лог-сообщения только с указанного контейнера.


Сбор лог-сообщений с использованием подсказок (hints)


Filebeat поддерживает автообнаружение на основе подсказок.
Он ищет информацию (подсказки) о конфигурации сбора в лейблах контейнера.
Как только контейнер запустится, Filebeat проверит, содержит ли он какие-либо подсказки, и запустит для него сбор с правильной конфигурацией.


Подробнее можно почитать здесь.


Настройка сбора состоит из следующих шагов:


  1. Удаляем шаблон обнаружения сервиса app и включаем подсказки:
    filebeat.docker.yml


    filebeat.autodiscover:
      providers:
        - type: docker
          hints.enabled: true
    
    # Пока будем выводить лог-сообщения в консоль
    output.console:
      pretty: true

  2. Отключаем сбор лог-сообщения для сервиса log-shipper:
    docker-compose.yml


    version: "3.8"
    
    services:
      app:
        ...
    
      log-shipper:
        ...
        labels:
          co.elastic.logs/enabled: "false"


Парсинг лог-сообщений


Для обработки лог-сообщений в Filebeat есть большое количество обработчиков (processors).
Их можно подключить с помощью лейблов контейнеров либо определить в конфигурационном файле.
Воспользуемся вторым способом.


  1. Для начала очистим лог-сообщения от метаданных. Для этого в конфигурационный файл добавим обработчик drop_fields:
    filebeat.docker.yml


    processors:
      - drop_fields:
          fields: ["agent", "container", "ecs", "log", "input", "docker", "host"]
          ignore_missing: true

    Теперь лог-сообщение выглядит следующим образом:


    {
      "@timestamp": "2021-04-01T04:02:28.138Z",
      "@metadata": {
        "beat": "filebeat",
        "type": "_doc",
        "version": "7.12.0"
      },
      "message": "app-log - ERROR - [Item not found] - 1",
      "stream": ["stdout"]
    }

  2. Для отделения лог-сообщений API от лог-сообщений asgi-сервера, добавим к ним тег с помощью обработчика add_tags:
    filebeat.docker.yml


    processors:
      - drop_fields:
          ...
      - add_tags:
        when:
          contains:
            "message": "app-log"
        tags: [test-app]
        target: "environment"

  3. Структурируем поле message лог-сообщения с помощью обработчика dissect и удалим его с помощью drop_fields:
    filebeat.docker.yml


    processors:
      - drop_fields:
        ...
      - add_tags:
        ...
     - dissect:
         when:
           contains:
             "message": "app-log"
         tokenizer: 'app-log - %{log-level} - [%{event.name}] - %{event.message}'
         field: "message"
         target_prefix: ""
     - drop_fields:
         when:
           contains:
             "message": "app-log"
         fields: ["message"]
         ignore_missing: true

    Теперь лог-сообщение выглядит следующим образом:


    {
      "@timestamp": "2021-04-02T08:29:07.349Z",
      "@metadata": {
        "beat": "filebeat",
        "type": "_doc",
        "version": "7.12.0"
      },
      "log-level": "ERROR",
      "event": {
        "name": "Item not found",
        "message": "Foo"
      },
      "environment": [
        "test-app"
      ],
      "stream": "stdout"
    }


Дополнение


Filebeat так же имеет готовые решения сбора и парсинга лог-сообщений для широко используемых инструментов таких, как Nginx, Postgres и т.д.


Они называются модулями.


К примеру для сбора лог-сообщений Nginx, достаточно добавить к его контейнеру лейбл:


  co.elastic.logs/module: "nginx"

и включить подсказки в конфигурационном файле. После этого мы получим готовое решение для сбора и парсинга лог-сообщений + удобный dashboard в Kibana.


Всем спасибо за внимание!