LINUX.ORG.RU

А как бы вы оттранкейтили начало файла, в который пишет процесс?

 , ,


0

1

Есть достаточно нетривиальная задача, если найдется решение хотя бы для linux - было бы уже неплохо.

Есть процесс, запущенный через fork() + close(1, 2) + open(file1, file2) + execve. Соответственно он пишет свой stdout/err в файл.

Что нужно - периодически чекать размер файла в другой программе (тривиально) и если он больше чем N (пусть будет 10 МБ) - отрезать ему M байт (пусть будет 5 МБ) с начала (не тривиально). Оставив таким образом только последние 5 МБ её выхлопа.

Не тривиально, потому что этот же файл всё еще пишется запущенной исходной программой.

Да, можно в теории поступить по-другому - открыть файл, скопировать его в другой, сохранить как .log.1, затранкейтив полностью исходный. Но есть проблема в том, что таких «пишущих» процессов будет что-то около 50 и засирать каталог очень не хочется (тем более, что практика показала, что искать что-то одновременно в log.1 … log.9 не очень удобно (особенно когда это что-то попадает куском в первый файл, а куском во второй)).

logrotate не доступен, потому что это условно-portable сервис + его надо запускать под виндами, желательно не переписав его на 90%.


UPD:
В общем на чем сейчас остановились: перехватывать на себя выхлоп через пайпы - не вариант, нужно плодить миллион (80 шт) нитей чтобы обслуживать все запущенные сервисы (40 шт).
Сейчас рассматриваем вариант с одной нитью и работать как logrotate - делать log.1, log.2 для всех разом. Непенятно только как заставить каретку в программе переехать на начало, ведь если работающий сервис не сделал close - то его позиция каретки зафикшена и если после моего truncate файла будет write со стороны программы - то там будет не 0, а файл, размером N МБ + добавка последней записи.
Кто расскажет, какую магию делает logrotate и logrotatewin? https://pastebin.com/ewDukJJx

★★★★★

Последнее исправление: PPP328 (всего исправлений: 2)

Что нужно - периодически чекать размер файла в другой программе (тривиально) и если он больше чем N (пусть будет 10 МБ) - отрезать ему M байт (пусть будет 5 МБ) с начала (не тривиально). Оставив таким образом только последние 5 МБ её выхлопа.

Всмысле? Пока процесс файл не отпустил место на диске Вам никто не вернёт (даже если Вы файл удалите). Можно конечно в нём пытаться делать дыры из параллельного процесса, но я не уверен что Вы этого хотите.

bugfixer ★★★★★
()

Не тривиально, потому что этот же файл всё еще пишется запущенной исходной программой.

Надо, чтобы исходная программа умела по сигналу переоткрывать файл и начинать писать с 5 МБ. Тогда переименовываешь файл, создаёшь пустой, посылаешь сигнал, заполняешь первые 5 МБ. Иначе без потерь никак.

monk ★★★★★
()
Ответ на: комментарий от monk

Можно чтобы просто переоткрывала. Тогда готовишь файл размером 5МБ, переименовываешь рабочий во временный, затем подготовленный в рабочий. Посылаешь сигнал. Из временного переливаешь данные, удаляешь.

monk ★★★★★
()
Ответ на: комментарий от bugfixer

Пока процесс файл не отпустил место на диске Вам никто не вернёт (даже если Вы файл удалите).

При обрезании размера возвращает. У logrotate есть copytruncate. Работает.

monk ★★★★★
()

его надо запускать под виндами

По умолчанию Вам даже открыть не дадут файл, если он открыт другим процессом. Но это настраивается в параметрах CreateFile.

Возможно, стоит взять SQLite + WAL, где танцы с блокировками при работе сразу нескольких программ с одной базой для всех ОС уже решены?

anonymous
()

Можно попробовать подсунуть пайп и перенаправлять логи в какую нибудь БД для временных рядов.

TDrive ★★★★★
()
Ответ на: комментарий от TDrive

только там с блокировкой записи в файл нужно быть осторожным)) из пайпа обязательно кто то должен читать

TDrive ★★★★★
()
Ответ на: комментарий от anonymous

Возможно, стоит взять SQLite + WAL, где танцы с блокировками при работе сразу нескольких программ с одной базой для всех ОС уже решены?

Там с обрезанием размера файла тоже нетривиально.

monk ★★★★★
()

Если можно править само приложение, то ротацию лучше организовать прямо в нём, раз оно портативное. А внутри него уже сделать опции отдельно на запись в файлы с ограничением размера, отдельно на stdout.

А как бы вы оттранкейтили начало файла, в который пишет процесс?

Я бы сделал так.

apt_install_lrzsz ★★★
()

пусть пишет в vfs.

найти подходящую в этих ваших интернетах или сделать самому на коленке по «howto create virtual file system in linux»

«если файл открыт только одним процессом и его размер больше XXX мег, то обрезать лишнее (или просто физически писать в следующую пачку, а эту переименовать)»

MKuznetsov ★★★★★
()
Ответ на: комментарий от anonymous

Например, сделать журнал как кольцевой буфер нужного размера.

Причём управление этим журналом можно сделать отдельной приблудой, а stdout сабжевой проги перенаправлять не в файл, а на вход приблуды – например вместо open(file1, file2) написать open(/run/приблуда.socket, /run/приблуда.socket2).

dimgel ★★★★★
()

если файл предполагается не сильно большим, то я бы посмотрел в сторону ringbuffer+mmap

но тут изначальная потребность немного кривая. какой смысл в «последних» 5МБ? стандартная практика N-файлов по M-размеру (или T-времени). какой-то кривой велосипед изобретается, имхо. особливо если речь про 50 процессов. тут прям напрашивается использование стандартного системного логгера, а он уж сам отротейтит как полагается.

ergo ★★★
()
Последнее исправление: ergo (всего исправлений: 2)
Ответ на: комментарий от ergo

использование стандартного системного логгера

Решение должно быть кроссплатформенным, под виндами такого нет.

какой смысл в «последних» 5МБ?

Примерно неделя логов.

PPP328 ★★★★★
() автор топика

Без анальных плясок — никак.

Можно воспользоваться тем фактом, что I/O offset — это часть file description, а не file descriptor. Пишем программу-обёртку, которая будет хранить дубликаты файловых дескрипторов, полученных после open. Время от времени проверяем размер, если он выше порога — вручную копируем диапазон с 5M до конца по смещению 0 и делаем lseek + truncate.

Проблема в том, что тут огромная такая дыра с гонкой.


UPD: А, можешь ещё попробовать fallocate(FALLOC_FL_COLLAPSE_RANGE) + lseek(SEEK_CUR, -5*1024*1024). Хотя тут тоже гонка, но гораздо меньше.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от intelfx

Каждый лог-файл представляет собой очередь, голова в некоторый момент времени укорачивается, а к хвосту файла добавляется новая информация.

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

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

iZEN ★★★★★
()
Ответ на: комментарий от intelfx

Очевидный btrfs забыл

Btrfs не годна в продакшен, забыл?

iZEN ★★★★★
()

Какой только фигней не страдают. Если нужна неделя логов, пиши в файл logs/.log, и позапрошлую удаляй при запуске.

anonymous
()

На каждый файл завести по нити-монитору изменений.

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

Собранные данные должны будут быть доступны нити-супервизору, которая будет «посещать» каждую нить и выспрашивать у неё свежую запись для обработки.

Вся эта машинерия строится на принципах синхронизации и блокировок по схеме «Много писателей - один читатель». Прокси-нити нужны, чтобы надолго не блокировать от изменений лог-файлы, когда до них дойдёт очередь.

iZEN ★★★★★
()

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

anymouse
()
Ответ на: комментарий от TDrive

Можно попробовать подсунуть пайп и перенаправлять логи в какую нибудь БД для временных рядов.

Проработали этот вариант, столкнулись с проблемами. Запускаемых приложений около 40. При запуске подменяем stdout/err на пайп под linux, в CreateProcess указываем пайпы как выходные stdout/err.

И тут засада. Из пайпа кто-то должен читать. Это не сложно. Создаем нить, которая будет постоянно вычитывать выходной пайп. Опустим проблемы синхронизации подмены пайпа в нити когда приложение падает и нужно переподнимать канал. Всё равно как ни крути нужны две нити - для stdout и stderr раздельно, иначе друг друга блокируют.

Итого получаем 80 нитей, которые постоянно долбят read чтобы прочитать выхлоп и положить его в файл.

Плохо.

PPP328 ★★★★★
() автор топика
Ответ на: комментарий от anymouse

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

Программа при запуске не в курсе что пишет в файл, её запускают и переопределяют stdout/err в файлы самостоятельно. По факту она просто срет в stdout/err. Это поменять нельзя - потому что иначе если запускать их много разом они будут друг другу мешать, потому что про друг друга они не в курсе.

PPP328 ★★★★★
() автор топика
Ответ на: комментарий от zudwa

Программы пишут в stdout/err, дескриптор в таком случае закрывать нельзя.

supervisord такой выхлоп умеет переопределять в файл и ротейтить несмотря на открытый дескриптор. Как?

UPD:
Посмотрел в исходниках. Он перехватывает stdout/err в pipe:

    def _prepare_child_fds(self):
        options = self.config.options
        options.dup2(self.pipes['child_stdin'], 0)
        options.dup2(self.pipes['child_stdout'], 1)
        if self.config.redirect_stderr:
            options.dup2(self.pipes['child_stdout'], 2)
        else:
            options.dup2(self.pipes['child_stderr'], 2)
        for i in range(3, options.minfds):
            options.close_fd(i)
PPP328 ★★★★★
() автор топика
Последнее исправление: PPP328 (всего исправлений: 1)

А чего не портануть syslogd и вызов в винду? еще в 90-х так делали, работало. Даже сеточка работала. Думаю готовых портов валом.

slapin ★★★★★
()
Ответ на: комментарий от PPP328

Итого получаем 80 нитей, которые постоянно долбят read чтобы прочитать выхлоп и положить его в файл.

А чем плохо? Они же просто висят в ожидании данных.

TDrive ★★★★★
()
Ответ на: комментарий от PPP328

Удаляй по таймеру.

(Маркдаун съел угловые скобки, там было <weeknum>.log)

anonymous
()

На правах упоротого школьника

Можно писать логи в кольцевой буфер и периодически сбрасывать его на диск/консоль. Молись о 100% аптайме :)

algamest
()
Ответ на: комментарий от TDrive

Оккам.jpg.

Сейчас прорабатываем неблокирующие чтения из пайпа. Решаем проблему того что в долбаном windows анонимные пайпы не могут быть async. Смешно, с учетом того, что в драйвере анонимные и именованные пайпы - одно и то же.

PPP328 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.