В целом, как и все остальные, держите файл открытым для производительности (открытие - относительно медленная операция). Однако вам нужно подумать о том, что произойдет, если вы оставите файл открытым, и люди либо удалят файл журнала, либо усекают его. И это зависит от флагов, используемых в открытом времени. (Я обращаюсь к Unix - аналогичные соображения, вероятно, применимы к Windows, но я приму исправление для тех, кто более осведомлен, чем я).
Если кто-то увидит, что файл журнала увеличивается, скажем, до 1 МБ, а затем удаляет его, приложение не станет мудрее, и Unix сохранит данные журнала в безопасности, пока приложение не закроет журнал. Более того, пользователи будут сбиты с толку, потому что они, вероятно, создали новый файл журнала с тем же именем, что и старый, и недоумевают, почему приложение «перестало регистрировать». Конечно, это не так; это просто запись в старый файл, который никто другой не может получить.
Если кто-то заметит, что размер файла журнала вырос, скажем, до 1 МБ, а затем урезает его, приложение также не будет мудрым. В зависимости от того, как был открыт файл журнала, вы можете получить странные результаты. Если файл не был открыт с помощью O_APPEND (POSIX-речь), то программа продолжит запись с его текущим смещением в файле журнала, и первые 1 МБ файла появятся в виде потока нулевых байтов, что путать программы, смотрящие на файл.
Как избежать этих проблем?
- Откройте файл журнала с помощью O_APPEND.
- Периодически используйте
fstat()
в дескрипторе файла и проверяйте, равен ли st_nlink
ноль.
Если количество ссылок становится равным нулю, кто-то удалил ваш файл журнала. Время, чтобы закрыть его и открыть новый. По сравнению с stat()
или open()
, fstat()
должно быть быстрым; это в основном копирование информации непосредственно из содержимого, которое уже находится в памяти, поиск имени не требуется. Поэтому вам, вероятно, следует делать это каждый раз, когда вы собираетесь писать.
Предложения:
- Убедитесь, что существует механизм, позволяющий программе переключать журналы.
- Убедитесь, что в сообщениях указана полная дата и время.
Я страдаю от приложения, которое выдает время, а не дату. Ранее сегодня у меня был файл сообщений, в котором были некоторые записи от 17 августа (одно из сообщений случайно включило дату в сообщение по истечении времени), а затем некоторые записи с сегодняшнего дня, но я могу сказать это только потому, что создал их. Если бы я посмотрел файл журнала через несколько недель, я не смог бы сказать, в какой день они были созданы (хотя я знал бы время, когда они были созданы). Такие вещи раздражают.
Вы также можете посмотреть, что делают такие системы, как Apache - у них есть механизмы для обработки файлов журналов и есть инструменты для работы с ротацией журналов. Примечание: если приложение сохраняет один файл открытым, не использует режим добавления и не планирует ротацию журналов или ограничения по размеру, тогда вы мало что можете сделать с увеличением размера файлов журналов или наличием нулей в начале - другое чем перезапуск приложения периодически.
Вы должны убедиться, что все записи в журнал завершены как можно скорее. Если вы используете файловые дескрипторы, в этом случае только буферизация ядра; это вполне может быть приемлемо, но рассмотрите варианты O_SYNC
или O_DSYNC
до open()
. Если вы используете файловый поток ввода / вывода, убедитесь, что за каждой записью следует fflush()
. Если у вас многопоточное приложение, убедитесь, что каждое write()
содержит полное сообщение; не пытайтесь писать части сообщения отдельно. При файловом потоке ввода / вывода вам может потребоваться использовать flockfile()
и родственников для группировки операций вместе. С помощью файлового дескриптора ввода / вывода вы можете использовать dprintf()
для выполнения форматированного ввода / вывода для файлового дескриптора (хотя не совсем ясно, что dprintf()
делает один вызов write()
) или, возможно, writev()
для записи отдельных сегментов данных в одной операции.
Кстати, дисковые блоки, которые «содержат» нули, фактически не выделяются на диске. Вы действительно можете испортить стратегии резервного копирования людей, создав файлы по несколько ГиБ каждый, но все, кроме самого последнего блока диска, содержат только нули. В основном (проверка ошибок и генерация имени файла для краткости опущены):
int fd = open("/some/file", O_WRITE|O_CREATE|O_TRUNC, 0444);
lseek(fd, 1024L * 1024L * 1024L, 0);
write(fd, "hi", 2);
close(fd);
Это занимает один блок диска на диске - но 1 ГБ (и изменение) при резервном копировании (без сжатия) и 1 ГБ (и изменение) при восстановлении. Антисоциально, но возможно.