面试题答案
一键面试1. 选择合适的Stream类型
在Node.js中,fs.createWriteStream
可以用于写入日志文件,它是一个可写流(Writable Stream)。为了高效处理高并发写入,可以利用其 highWaterMark
和 pipe
等特性。
2. 确保日志记录的准确性和高效性
- 缓冲写入:可写流的
highWaterMark
默认值为64KB,可以根据实际情况调整这个值,以平衡内存使用和写入效率。当写入的数据量超过highWaterMark
时,write
方法会返回false
,此时可以暂停写入,等待drain
事件触发后再继续写入。 - 事件监听:监听
drain
事件来判断缓冲区是否有空间继续写入,避免数据丢失。 - 使用
pipe
:如果需要对日志进行预处理(如格式化),可以使用pipe
将可读流(如process.stdout
或者自定义的日志生成流)连接到可写流(日志文件写入流),这样数据会自动流动,减少手动处理的复杂度。
3. 处理日志文件大小限制和滚动日志
- 文件大小监控:可以使用
fs.stat
来获取当前日志文件的大小。定时(如每隔一定时间或者每次写入后)检查文件大小,如果超过设定的限制(如100MB),则进行滚动日志操作。 - 滚动日志实现:
- 重命名文件:将当前日志文件重命名,例如在文件名后添加时间戳,如
app.log
重命名为app_20231001120000.log
。 - 创建新文件:创建一个新的日志文件继续写入。在Node.js中,通过
fs.rename
重命名文件,通过fs.createWriteStream
创建新的日志文件。 - 使用第三方库:也可以使用成熟的日志库,如
winston
或pino
,它们内置了日志滚动的功能。例如,winston
可以通过配置maxsize
和maxFiles
等参数来实现日志文件大小限制和滚动日志。
- 重命名文件:将当前日志文件重命名,例如在文件名后添加时间戳,如
以下是一个简单的示例代码(不使用第三方库):
const fs = require('fs');
const path = require('path');
const logFilePath = path.join(__dirname, 'app.log');
const maxLogSize = 1024 * 1024 * 100; // 100MB
let logStream = fs.createWriteStream(logFilePath, { flags: 'a', highWaterMark: 16384 });
function writeLog(message) {
const writeResult = logStream.write(`${new Date().toISOString()} - ${message}\n`);
if (!writeResult) {
logStream.once('drain', () => {
// 缓冲区有空间后继续写入
});
}
checkLogSize();
}
function checkLogSize() {
fs.stat(logFilePath, (err, stats) => {
if (err) {
return;
}
if (stats.size >= maxLogSize) {
const newLogFilePath = path.join(__dirname, `app_${Date.now()}.log`);
fs.rename(logFilePath, newLogFilePath, (renameErr) => {
if (renameErr) {
return;
}
logStream.end();
logStream = fs.createWriteStream(logFilePath, { flags: 'a', highWaterMark: 16384 });
});
}
});
}
// 示例使用
writeLog('This is a test log');
通过以上设计,可以在高并发的Web应用中准确且高效地记录日志,并处理日志文件大小限制和滚动日志的需求。