Вопрос или проблема
Я использую 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 в файл с использованием методов, описанных выше, позволит вам эффективно отслеживать события в контейнере в реальном времени, что значительно упростит процесс отладки и мониторинга. Обязательно протестируйте указанные методы, чтобы выбрать наиболее подходящий для вашего рабочего процесса.