Как записывать логи Docker в файл в реальном времени (à la tail -f)

Вопрос или проблема

Я использую docker, который выводит логи в stdout и stderr, которые можно просмотреть с помощью:

docker logs -f $LOGS_CONTAINER_ID

Я также добавил ‘sed’, который добавляет идентификатор контейнера в начале каждой строки:

docker logs -f $LOGS_CONTAINER_ID | sed "s/^/$LOGS_CONTAINER_ID /"

Если я запущу это, я получаю что-то вроде:

container112 error 10:20:10 проблема
container112 info 10:20:09 не проблема
container112 error 10:20:01 проблема

где “container112” — это $LOGS_CONTAINER_ID.

ПОКА ВСЕ НОРМАЛЬНО. Теперь я хочу вывести вышеуказанную команду в файл (log.out),
поэтому я написал следующую команду:

docker logs -f $LOGS_CONTAINER_ID | sed "s/^/$LOGS_CONTAINER_ID /" >> log.out

Что происходит, так это то, что лог записывается в log.out, но новые логи не появляются (если я открываю новую сессию и запускаю tail -f log.out, я не вижу вывода).
Поэтому я также попробовал:

tail -f $(docker logs -f $LOGS_CONTAINER_ID | sed "s/^/$LOGS_CONTAINER_ID /") >> log.out

Но это тоже не сработало.
В чем проблема?

Поможет ли это?

docker logs $CONTAINERID 2>&1 | cat > logfile.log

Я думаю, что здесь происходит две вещи.

Как сказал @paul-oskar-mayer в своем ответе, логи docker на самом деле записываются в stderr, поэтому команды docker должны перенаправлять stderr в stdout, чтобы их можно было обработать с помощью sed и записать в файл, как это:

docker logs -f $LOGS_CONTAINER_ID 2>&1 | sed "s/^/$LOGS_CONTAINER_ID /" >> log.out
                                  ^^^^

Однако, когда вы это делаете, вывод из docker logs не появляется в лог-файле сразу; он буферизуется и записывается периодически. Если вы выполните вышеуказанную команду и tail -f log.out, вы увидите множество строк вывода, записанных одновременно. Если вы пытаетесь что-то отладить в спешке, вы можете легко завершить работающую команду до того, как какой-либо вывод будет записан в файл.

Способ обойти это — использовать команду unbuffer (https://linux.die.net/man/1/unbuffer), на Ubuntu 24.04 это была часть пакета expect. Это должно использоваться в последней команде в конвейере перед записью потока в файл.

docker logs -f $LOGS_CONTAINER_ID 2>&1 | unbuffer -p sed "s/^/$LOGS_CONTAINER_ID /" >> log.out
                                         ^^^^^^^^^^^

—- Редактирование
@filbranden предложил лучшее решение, чем использование “unbuffer”, и он прав, что это дубликат, извините.

docker logs -f $LOGS_CONTAINER_ID 2>&1 | sed -u "s/^/$LOGS_CONTAINER_ID /" >> log.out
                                             ^^

Ответ или решение

Запись логов Docker в файл в реальном времени

Если вы работаете с Docker и хотите записывать логи контейнера в файл в реальном времени, аналогично команде tail -f, то вам потребуются некоторые знания о том, как корректно обрабатывать стандартный вывод и стандартный поток ошибок. Это важно, потому что логи Docker могут выводиться как в стандартный вывод (stdout), так и в стандартный поток ошибок (stderr).

1. Базовая команда для записи логов

Используйте следующую команду для получения логов контейнера:

docker logs -f $LOGS_CONTAINER_ID

Чтобы добавить идентификатор контейнера к каждой строке, вы можете использовать sed:

docker logs -f $LOGS_CONTAINER_ID | sed "s/^/$LOGS_CONTAINER_ID /"

Однако, при попытке записать вывод в файл с помощью следующей команды, вы заметите, что новые логи могут не добавляться в файл в реальном времени из-за буферизации:

docker logs -f $LOGS_CONTAINER_ID | sed "s/^/$LOGS_CONTAINER_ID /" >> log.out

2. Проблема с буферизацией

Когда вы записываете вывод в файл, многие инструменты (включая sed) могут не записывать данные немедленно. Вместо этого они могут накапливать данные в памяти и записывать их в файл раз в несколько секунд. Это не подходит, если вам нужны логи в реальном времени.

3. Решение проблемы с буферизацией

Для решения этой проблемы вы можете использовать два подхода:

Подход 1: Использование unbuffer

Можно использовать команду unbuffer из пакета expect, чтобы отключить буферизацию. Установите пакет, если он еще не установлен:

sudo apt-get install expect

Затем измените вашу команду следующим образом:

docker logs -f $LOGS_CONTAINER_ID 2>&1 | unbuffer -p sed "s/^/$LOGS_CONTAINER_ID /" >> log.out

Подход 2: Использование sed -u

Альтернативно, вы можете использовать опцию -u для sed, которая включает небуферизированный режим:

docker logs -f $LOGS_CONTAINER_ID 2>&1 | sed -u "s/^/$LOGS_CONTAINER_ID /" >> log.out

Это обеспечит немедленную запись всех новых логов в файл log.out, и вы сможете использовать команду tail -f на этом файле для мониторинга в реальном времени:

tail -f log.out

4. Полная команда

Ваш окончательный вариант команды может выглядеть следующим образом:

docker logs -f $LOGS_CONTAINER_ID 2>&1 | sed -u "s/^/$LOGS_CONTAINER_ID /" >> log.out

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...