Если использовать docker swarm, docker отдаёт json с полями log, container_id и container_name. В Graylog, Kibana если нужно получить лог по определённому контейнеру, то приходится фильтровать по container_name. Но при рестарте сервиса имя контейнера меняется и query превращается в тыкву. Да и каждый раз нужно искать новое имя контейнера.

Показываю, как вытащить имя сервиса из container_name с помощью filter в fluentd. И добавить отдельным полем, по которому можно будет нормально фильтровать.

Сделаем простой docker-compose для теста:

version: '3'

services:
  nginx:
    image: nginx
    ports:
      - "80:80"
    logging:
      driver: "fluentd"
      options:
        fluentd-async-connect: "true"
        fluentd-sub-second-precision: "true"

Конфиг fluentd для дебага. Собирает всё, что ему шлёт наш сервис и отсылает в свой лог

# Source from docker
<source>
  @type forward
  port 24224
  tag docker
</source>

# Forward to stdout
<match docker>
  @type stdout
</match>

Отправляем запрос curl’ом и смотрим лог

tail -f /var/log/td-agent/td-agent.log

2022-12-09 16:20:46.897530693 +0000 docker: {"source":"stdout","log":"10.0.0.2 - - [09/Dec/2022:16:20:46 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"curl/7.68.0\" \"-\"","container_id":"8e800da34b65b5cf5f883a7367e844131b40d64fccc62f62d802a92199e4bf3c","container_name":"/nginx_nginx.1.cfl93fy7a9qiupzlhs89ee0uk"}

Имя сервиса можно извлечь из container_name. Для этого будем использовать filter с плагином record_transformer, с помощью которого можно добавить в логи ещё одно поле.

В документации показано, что из “tag” можно вытащить один элемент tag_parts[N]. Но у нас это не tag, а строка. Со строкой можно работать с помощью функций Ruby.

Нам нужно вытащить nginx_nginx из /nginx_nginx.1.cfl93fy7a9qiupzlhs89ee0uk

Добавляем filter

# Add Service name
<filter docker>
  @type record_transformer
  enable_ruby true
  <record>
    service_name ${ s = record["container_name"][1..-1].split("\.")[0]}
 </record>
</filter>
  • enable_ruby true - эта строка добавляет возможность использования функций Ruby
  • record["container_name"] - так забираем строку из другого поля
  • С помощью [1..-1] удаляем первый символ в строке “/”. Нафиг он вообще там нужен?
  • split("\.")[0] - с помощью этой функции делаем как бы массив из строки и берём нулевое значение

Получаем такой конфиг

# Source from docker
<source>
  @type forward
  port 24224
  tag docker
</source>

# Add Service name
<filter docker>
  @type record_transformer
  enable_ruby true
  <record>
    service_name ${ s = record["container_name"][1..-1].split("\.")[0]}
 </record>
</filter>

# Forward to stdout
<match docker>
  @type stdout
</match>

Перезапускаем агент, отправляем запрос и получаем:

2022-12-09 16:33:30.860224690 +0000 docker: {"container_name":"/nginx_nginx.1.cfl93fy7a9qiupzlhs89ee0uk","source":"stdout","log":"10.0.0.2 - - [09/Dec/2022:16:33:30 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"curl/7.68.0\" \"-\"","container_id":"8e800da34b65b5cf5f883a7367e844131b40d64fccc62f62d802a92199e4bf3c","service_name":"nginx_nginx"}

Получили дополнительное поле "service_name":"nginx_nginx", по которому теперь можно нормально фильтровать логи.

Мой телеграм-канал .