Войти через FIFO, а затем перенаправить в файл?

8

У меня есть приложение, которое должно регистрировать каждую транзакцию. Каждое сообщение журнала сбрасывается, потому что мы должны иметь запись о том, что произошло до сбоя. Мне и моим коллегам было любопытно, как добиться эффекта производительности буферизации, гарантируя, что сообщение журнала вышло из процесса.

Что мы придумали это:

  • сделать FIFO, в который приложение может написать, и
  • перенаправить содержимое этого FIFO в обычный файл через cat.

То есть то, что обычно было:

app --logfile logfile.txt

сейчас:

mkfifo logfifo
cat logfifo &> logfile.txt &
app --logfile logfifo

Есть ли какие-либо ошибки в этом подходе? Это сработало, когда мы протестировали его, но мы хотим быть абсолютно уверены, что сообщения попадут в файл перенаправления даже в случае сбоя исходного приложения.

(У нас нет исходного кода для приложения, поэтому о программных решениях не может быть и речи. Кроме того, приложение не будет записывать в него stdout, поэтому вопрос о передаче напрямую другой команде исключен. Поэтому syslogэто невозможно .)


Обновление: я добавил награду. Принятый ответ будет не включать в себя loggerпо той простой причине , что loggerэто не то , что я спросил о. Как говорится в первоначальном вопросе, я ищу только ошибки в использовании FIFO.

chrisaycock
источник
Кажется, что в некоторых ответах пока что есть некоторая путаница. Как я уже отмечал в своем вопросе, я хотел бы найти подход к использованию FIFO в качестве посредника при ведении журнала . Я не заинтересован в альтернативных предложениях, которые, по крайней мере, не изучают ошибки моего первоначального вопроса.
chrisaycock
Если мой ответ о входе в stdout - это не то направление, которое вас интересует, не могли бы вы удалить «Кроме того, приложение не будет писать в stdout, поэтому вопрос о передаче напрямую другой команде исключен» из вопроса?
Никгрим

Ответы:

6

Обратите внимание, что в программировании обычно требуется fifo, когда записанное количество может превышать считываемое.

Таким образом, fifo не будет работать абсолютно гладко, как вы ожидаете, но решит вашу главную проблему, представляя другую.

Есть три возможных предостережения.

  1. Запись в fifo блокируется на неопределенный срок, если ничто не читает другой конец при инициализации.
  2. FIFO имеет фиксированную ширину 64 КБ, благодаря которой, если буфер заполнен этой точкой, дальнейшие записи будут блокироваться до тех пор, пока читатель не догонит.
  3. Канальный писатель будет убит SIGPIPE, если читатель умрет или выйдет.

Это будет означать, что ваша проблема (эмулировать буферизованный ввод-вывод при небуферизованной записи) будет решена. Это связано с тем, что новый «предел» в вашем FIFO фактически станет скоростью, с которой любая утилита записывает то, что находится в канале на диск (который, вероятно, будет буферизован для ввода-вывода).

Тем не менее, писатель становится зависимым от вашего читателя журнала для работы. Если читатель перестает внезапно читать, писатель будет блокировать. Если считыватель внезапно завершает работу (допустим, у вас заканчивается свободное место на диске), то пишущий SIGPIPE и, вероятно, завершит работу.

Еще один момент, который стоит упомянуть: если сервер паникует, а ядро ​​перестает отвечать, вы можете потерять до 64 КБ данных, которые были в этом буфере.

Другой способ исправить это - записать логи в tmpfs (/ dev / shm в linux) и привязать вывод к фиксированному месту на диске. При этом существуют менее строгие ограничения на выделение памяти (не 64 КБ, как правило, 2 ГБ!), Но он может не сработать, если у автора нет динамического способа повторного открытия файлов журналов (вам придется периодически очищать журналы от tmpfs). Если сервер паникует в этом методе, вы можете потерять ОЧЕНЬ больше данных.

Мэтью Ифе
источник
Это хорошая информация относительно /dev/shm. Мы тоже обдумали это, хотя это ускользнуло от меня tail -f.
chrisaycock
4
mkfifo logfifo
cat logfifo &> logfile.txt &
app --logfile logfifo

Что происходит, когда ваш cat logfifoпроцесс умирает, кто-то случайно его убивает или кто-то случайно указывает на неправильное место?

Мой опыт показывает, что appон быстро заблокируется и зависнет. Я попробовал это с Tomcat, Apache и несколькими небольшими домашними приложениями и столкнулся с той же проблемой. Я никогда не исследовал очень далеко, потому что loggerпростое перенаправление ввода / вывода делало то, что я хочу. Я обычно не нуждаюсь в полноте регистрации, которую вы пытаетесь использовать. И, как вы говорите, вы не хотите регистратор.

Существует некоторая дискуссия по этой проблеме в неблокирующем Linux fifo (по запросу) .

Стефан Ласевский
источник
Интересно. Таким образом, приложение может заблокировать в любом случае? Мы думаем о том, чтобы добавить полотенце и заказать Fusion IO.
chrisaycock
Попробуйте. Для меня этот результат произошел быстро (в тестовой среде на оборудовании низкой спецификации). Это было в Ubuntu и RedHat 5. Не уверен, что FreeBSD и т. Д. Ведут себя аналогично.
Стефан Ласевский
2

Ваши возможности довольно ограничены приложением, но то, что вы тестировали, будет работать.

Мы делаем что-то похожее с лаками и varnishncsa, чтобы логи get где-то были нам полезны. У нас есть fifo, и мы просто читаем его с помощью syslog-ng и отправляем туда, где нам нужно. Мы обрабатываем около 50 ГБ и до сих пор не сталкивались с такой проблемой

липкая
источник
Круто, рад слышать, что мы не единственные, кто придумал это.
chrisaycock
2

Среда - CentOS, и приложение записывает в файл ...

Вместо отправки в обычный файл, я бы отправил вывод в syslog и убедился, что сообщения syslog отправляются на центральный сервер, а также локально.

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

logger -p daemon.notice -t app < fifo

Вы также можете получить вход (в logger) из catили tail -fв трубу:

tail -f fifo | logger ...
cat fifo | logger ...

Единственная проблема заключается в том, что оно не дифференцируется в зависимости от важности сообщения журнала (все это ЗАМЕЧАНИЕ ), но, по крайней мере, оно регистрируется и также отправляется вне хоста на центральный сервер.

Настройка системного журнала зависит от того, какой сервер вы используете. Есть rsyslogи syslog-ng(оба очень способные).

РЕДАКТИРОВАТЬ : Пересмотрен после получения дополнительной информации от автора.

Mei
источник
Сообщения журнала записываются в файл, который мы можем выбрать, но приложение не будет писать в него stdout. ОС - это CentOS (я пометил вопрос как linux , но, думаю, это легко пропустить). Все сообщения имеют одинаковую важность, поэтому нет разницы между уведомлением и ошибкой в ​​этом приложении.
chrisaycock
Нет разницы между уведомлением и ошибкой? Ох, правда ? Нет разницы между Slow query detectedи Database halted?
Мэй
(Я обновил пост.)
Мэй
1

Ты пишешь:

Кроме того, приложение не будет писать в stdout...

Вы пробовали войти в "файл" /dev/stdout? Это может позволить вам сделать что-то вроде:

app --logfile /dev/stdout | logger -t app
nickgrim
источник
Это отличается от FIFO? Какие есть ошибки? Я не ищу альтернативу как таковую ; Я ищу понимание того, является ли подход, который я перечислил, надежным. Это все, что меня интересует.
chrisaycock
1
Да, это отличается от FIFO; по сути это файл, который подключен к STDOUT. Таким образом, вы можете использовать это для входа в STDOUT, а оттуда в системный журнал или тому подобное. Он предназначен для решения вашей первоначальной проблемы, но, надеюсь, более надежен, чем FIFO, благодаря меньшему количеству движущихся частей.
Никгрим
1

Нет смысла записывать данные в fifo и затем записывать их в файл другим процессом. Просто пишите напрямую в файл, и как только он write()вернется, он в руках ядра; он все равно запишет его на диск в случае сбоя приложения.

psusi
источник
Каждое сообщение журнала сбрасывается : это блокирует запись. Мы не имеем никакого контроля над этим, так как у нас нет исходного кода приложения.
chrisaycock
@ chrisaycock, верно ... так в чем же проблема?
psusi
Мне и моим коллегам было любопытно, как добиться эффекта производительности буферизации, гарантируя, что сообщение журнала вышло из процесса.
chrisaycock
@ chrisaycock, ооо, так что в настоящее время приложение использует O_SYNC или fsync()для ожидания каждой записи на диске, и вы не хотите, чтобы это произошло (риск потери в случае сбоя системы)?
psusi
1
@ chrisaycock, это не имеет ничего общего с интерфейсом накопителя, обычно это просто означает, что ядро ​​скопировало его в кэш файловой системы (и из приложения). Как я уже сказал в своем ответе изначально, после write()возвращения приложение может аварийно завершить работу, если оно того захочет - данные попадут на диск до тех пор, пока не произойдет сбой ядра.
psusi