Как добавить данные в файл в Node?
Я пытаюсь добавить строку в файл журнала. Однако функция writeFile каждый раз стирает содержимое файла перед записью новой строки.
fs.writeFile('log.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('Сохранено!');
}); // => log.txt очищен, содержит только 'Hello Node'
Есть ли у кого-нибудь идеи, как сделать это проще?
5 ответ(ов)
Когда вы хотите записывать данные в лог-файл, т.е. добавлять информацию в конец файла, никогда не используйте appendFile. Функция appendFile открывает дескриптор файла для каждой записи, которую вы добавляете в файл, и после некоторого времени у вас возникнет красивая ошибка EMFILE.
Также хочу отметить, что appendFile не проще в использовании, чем WriteStream.
Пример с использованием appendFile:
console.log(new Date().toISOString());
[...Array(10000)].forEach(function (item, index) {
fs.appendFile("append.txt", index + "\n", function (err) {
if (err) console.log(err);
});
});
console.log(new Date().toISOString());
На моем компьютере можно добавлять данные в файл до 8000 раз, а затем возникает следующая ошибка:
{ Error: EMFILE: too many open files, open 'C:\mypath\append.txt'
at Error (native)
errno: -4066,
code: 'EMFILE',
syscall: 'open',
path: 'C:\\mypath\\append.txt' }
Кроме того, appendFile будет записывать данные по мере своего выполнения, поэтому ваши логи не будут записываться с отметкой времени. Вы можете протестировать это, установив 1000 вместо 100000 - порядок записи будет случайным, в зависимости от доступа к файлу.
Если вы хотите добавлять данные в файл, вы должны использовать доступный поток, вот так:
var stream = fs.createWriteStream("append.txt", { flags: 'a' });
console.log(new Date().toISOString());
[...Array(10000)].forEach(function (item, index) {
stream.write(index + "\n");
});
console.log(new Date().toISOString());
stream.end();
Вы завершаете его, когда хотите. Вам даже не обязательно использовать stream.end(), поскольку по умолчанию стоит опция AutoClose: true, и ваш файл будет закрыт, когда завершится ваш процесс, что позволяет избежать открытия слишком большого количества файлов.
Ваш код, использующий createWriteStream, создает файловый дескриптор для каждой записи. Однако использование logStream.end() предпочтительнее, поскольку оно сообщает Node.js закрыть поток сразу после завершения записи.
Вот пример кода:
var fs = require('fs');
var logStream = fs.createWriteStream('log.txt', {flags: 'a'});
// Используйте {flags: 'a'} для добавления и {flags: 'w'} для перезаписи файла
logStream.write('Начальная строка...');
logStream.end('Это заключительная строка');
Таким образом, logStream.end() гарантирует, что поток завершится корректно и файл будет закрыт, что может помочь избежать утечек ресурсов.
Чтобы использовать флаги a+ для добавления и создания файла (если он не существует), вы можете использовать следующий код:
fs.writeFile('log.txt', 'Hello Node', { flag: "a+" }, (err) => {
if (err) throw err;
console.log('Файл создан, если он не существовал!!');
});
Обратите внимание, что флаг a+ позволяет вам открывать файл для чтения и записи. Если файл не существует, он будет создан. В случае возникновения ошибки, она будет выброшена, и вы получите сообщение в консоли.
Документация: Node.js fs.flags
Кроме appendFile, вы также можете передать флаг в writeFile, чтобы добавить данные в существующий файл.
fs.writeFile('log.txt', 'Hello Node', {'flag': 'a'}, function(err) {
if (err) {
return console.error(err);
}
});
Передав флаг 'a', данные будут добавлены в конец файла.
Ваш подход действительно интересен. Использование метода WriteStream без фактического закрытия дескриптора файла через stream.end() имеет свои преимущества, и ваша реализация с помощью cork/uncork может быть очень эффективной в определённых сценариях.
По вашему примеру, код выглядит довольно просто и понятно, но заметим, что вместо бесконечного цикла for(true) в production-коде действительно будет лучше обрабатывать сообщения из websocket. Также стоит отметить, что использование cork и uncork позволяет минимизировать нагрузку на оперативную память и более безопасно управлять процессом записи, что особенно важно при ведении логов.
Ваш код с записью выглядит следующим образом:
var stream = fs.createWriteStream("log.txt", {flags:'a'});
for(true) {
stream.cork();
stream.write("some content to log");
process.nextTick(() => stream.uncork());
}
uncork действительно выталкивает данные в файл в следующем такте, что может повысить общую производительность системы, особенно в условиях высокой нагрузки, как вы и упомянули, до 200 записей в секунду. Уменьшая использование оперативной памяти и повышая стабильность вашей системы в пиковые моменты, ваша реализация может стать отличным решением для приложения, где важно ведение логов, не теряя при этом производительности.
Так что ваша стратегия вполне оправдана и может быть рекомендована, особенно для тех случаев, когда требуется обработка высоких пиков записи и при меньшей активности.
Запись в файлы в Node.js
Как скачать файл с помощью Node.js (без использования сторонних библиотек)?
Как получить полный объект в console.log() Node.js, а не '[Object]'?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект
Как остановить Babel от трансформации 'this' в 'undefined' и добавления "use strict"