Nodejs监听日志文件的变化的过程解析


Posted in NodeJs onAugust 04, 2019

最近有在做日志文件的分析,其中有一个需求:A服务器项目需要用Nodejs监听日志文件的变化,当项目产生了新的日志信息,将新的部分通过socket传输到B服务器项目。socket暂时不做分析。

这个需求很简单,通过分析我们开始撸码吧。 在撸码的过程中还能巩固所学Nodejs的API,何乐而不为呢?

所用的API

fs.watchFile()

语法

fs.watchFile(filename[, options], listener)

参数解析

filename <string> | <Buffer> | <URL> ——文件名
options <Object>

 persistent <boolean> 默认值: true。——是否应该继续运行
 interval <integer> 默认值: 5007。——轮询目标的频率
listener <Function>

 current <fs.Stats> ——当前值
 previous <fs.Stats> ——之前值

监视 filename 的更改。 每当访问文件时都会调用 listener 回调。

listener 有两个参数,当前的 stat 对象和之前的 stat 对象

这些 stat 对象是 fs.Stat 的实例。

要在修改文件(而不仅仅是访问)时收到通知,则需要比较 curr.mtime 和 prev.mtime。

当 fs.watchFile 操作导致 ENOENT 错误时,它将调用一次监听器,并将所有字段置零(或将日期设为 Unix 纪元)。 如果文件是在那之后创建的,则监听器会被再次调用,且带上最新的 stat 对象。 这是 v0.10 之后的功能变化。

使用 fs.watch() 比 fs.watchFile fs.unwatchFile 更高效。 应尽可能使用 fs.watch 代替 fs.watchFile 和 fs.unwatchFile。

当 fs.watchFile() 正在监视的文件消失并重新出现时,第二次回调事件(文件重新出现)返回的 previousStat 会与第一次回调事件(文件消失)返回的 previousStat 相同。

这种情况发生在:

  • 文件被删除,然后又恢复。
  • 文件被重命名两次,且第二次重命名回其原来的名称。

例子

fs.watchFile('message.text', (curr, prev) => {
 console.log(`当前的最近修改时间是: ${curr.mtime}`);
 console.log(`之前的最近修改时间是: ${prev.mtime}`);
});

fs.open()

语法

fs.open(path[, flags[, mode]], callback)

参数解析

path <string> | <Buffer> | <URL> ——文件路径
flags <string> | <number> 默认值: 'r'。——文件系统标志
mode <integer> 默认值: 0o666(可读写)。——设置文件模式(权限和粘滞位),但仅限于创建文件的情况
callback <Function>

 err <Error> ——错误
 fd <integer>——文件系统流

fs.read()

语法

fs.read(fd, buffer, offset, length, position, callback)

参数解析

fd <integer> ——文件系统流
buffer <Buffer> | <TypedArray> | <DataView>——数据将写入的缓冲区
offset <integer>—— buffer 中开始写入的偏移量
length <integer>——要读取的字节数
position <integer>——从文件中开始读取的位置
callback <Function>

 err <Error>
 bytesRead <integer>
 buffer <Buffer>

fs.createReadStream()

语法

fs.createReadStream(path[, options])

参数解析

path <string> | <Buffer> | <URL>——文件路径
options <string> | <Object>

 flags <string> 默认值: 'r'。——文件系统标志
 encoding <string> 默认值: null。——字符编码
 fd <integer> 默认值: null。——文件系统流
 mode <integer> 默认值: 0o666。——设置文件模式(权限和粘滞位),但仅限于创建文件的情况
 autoClose <boolean> 默认值: true。——是否自动关闭文件描述符
 start <integer>——文件读取的开始位置
 end <integer> 默认值: Infinity。——文件读取的结束位置
 highWaterMark <integer> 默认值: 64 * 1024。

返回: <fs.ReadStream> 参阅可读流。

如果 autoClose 为 false,则即使出现错误,也不会关闭文件描述符。 应用程序负责关闭它并确保没有文件描述符泄漏。 如果 autoClose 设为 true(默认行为),则在 'error' 或 'end' 事件时将自动关闭文件描述符。

mode 用于设置文件模式(权限和粘滞位),但仅限于创建文件的情况。

例子

读取sample.txt文件的10个字符

fs.createReadStream('sample.txt', { start: 90, end: 99 });

readLine.createInterface

语法

readline.createInterface(options)

参数解析

options <Object>

  input <stream.Readable> 要监听的可读流。此选项是必需的。
  output <stream.Writable> 将逐行读取数据写入的可写流。
  completer <Function> 用于 Tab 自动补全的可选函数。
  terminal <boolean> 如果 input 和 output 应该被视为 TTY,并且写入 ANSI/VT100 转义码,则为 true。 默认值: 实例化时在 output 流上检查 isTTY。
  historySize <number> 保留的最大历史记录行数。 要禁用历史记录,请将此值设置为 0。 仅当用户或内部 output 检查将 terminal 设置为 true 时,此选项才有意义,否则根本不会初始化历史记录缓存机制。 默认值: 30。
  prompt - 要使用的提示字符串。默认值: '> '。
  crlfDelay <number> 如果 \r 与 \n 之间的延迟超过 crlfDelay 毫秒,则 \r 和 \n 将被视为单独的行尾输入。  crlfDelay 将被强制转换为不小于 100 的数字。 可以设置为 Infinity, 这种情况下, \r 后跟 \n 将始终被视为单个换行符(对于使用 \r\n 行分隔符的文件读取可能是合理的)。 默认值: 100。
  removeHistoryDuplicates <boolean> 如果为 true, 则当添加到历史列表的新输入行与旧的输入行重复时,将从列表中删除旧行。 默认值: false。
  escapeCodeTimeout <number> readline 将会等待一个字符的持续时间(当以毫秒为单位读取模糊键序列时,可以使用输入读取到目前为止形成完整的键序列,并且可以采取额外的输入来完成更长的键序列)。 默认值: 500。

文件系统标志

这个不需要司机,记住常见的即可,需要的时候查找。

当 flag 选项采用字符串时,可用以下标志:

'a' - 打开文件用于追加。如果文件不存在,则创建该文件。

'ax' - 与 'a' 相似,但如果路径已存在则失败。

'a+' - 打开文件用于读取和追加。如果文件不存在,则创建该文件。

'ax+' - 与 'a+' 相似,但如果路径已存在则失败。

'as' - 以同步模式打开文件用于追加。如果文件不存在,则创建该文件。

'as+' - 以同步模式打开文件用于读取和追加。如果文件不存在,则创建该文件。

'r' - 打开文件用于读取。如果文件不存在,则出现异常。

'r+' - 打开文件用于读取和写入。如果文件不存在,则出现异常。

'rs+' - 以同步模式打开文件用于读取和写入。指示操作系统绕过本地的文件系统缓存。

这对于在 NFS 挂载上打开文件时非常有用,因为它允许跳过可能过时的本地缓存。 它对 I/O 性能有非常实际的影响,因此除非需要,否则不建议使用此标志。

这不会将 fs.open() 或 fsPromises.open() 转换为同步的阻塞调用。 如果需要同步的操作,则应使用 fs.openSync() 之类的。

'w' - 打开文件用于写入。如果文件不存在则创建文件,如果文件已存在则截断文件。

'wx' - 与 'w' 相似,但如果路径已存在则失败。

'w+' - 打开文件用于读取和写入。如果文件不存在则创建文件,如果文件已存在则截断文件。

'wx+' - 与 'w+' 相似,但如果路径已存在则失败。

fs.Stats 类

fs.Stats 对象提供有关文件的信息。

Stats {
 dev: 2114,
 ino: 48064969,
 mode: 33188,
 nlink: 1,
 uid: 85,
 gid: 100,
 rdev: 0,
 size: 527,
 blksize: 4096,
 blocks: 8,
 atimeMs: 1318289051000.1,
 mtimeMs: 1318289051000.1,
 ctimeMs: 1318289051000.1,
 birthtimeMs: 1318289051000.1,
 atime: Mon, 10 Oct 2011 23:24:11 GMT,
mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT }

 开始监听日志文件

前提,在app.js中调用watchFile方法,将需要监听的文件路径传入该方法中。

function watchFile(filename) {
 console.log('Log monitoring...');
 // Open the file for reading and appending
 fs.open(filename, 'a+', function (err, fd) {
 if (err) {
  throw err;
 }
 var buffer;
 fs.watchFile(filename, {
  persistent: true,
  interval: 1000
 }, (curr, prev) => {
  // Compare the time before and after
  if (curr.mtime > prev.mtime) {
  // console.log(`The current latest revision time is: ${curr.mtime}`);
  // console.log(`The latest modification time is: ${prev.mtime}`);

  // Changes in the contents of documents
  buffer = new Buffer(curr.size - prev.size);
   // (curr.size - prev.size) this is the newly added length of the log file
  readFile(fd, buffer, (curr.size - prev.size), prev.size);
  }
 });
 });

}

读取新增内容

function readFile(fd, buffer, length, position) {
 // read file
 fs.read(fd, buffer, 0, length, position, function (err, bytesRead, buffer) {
 if (err) {
  log.error(err);
 }
 console.log('Additional Contents', buffer.toString());
 });
}

额外功能:读取历史内容

function fetchHistoryLogs(filename) {
 const rl = readLine.createInterface({
 input: fs.createReadStream(filename, {
  enconding: 'utf8'
 }),
 output: null,
 terminal: false
 });

 rl.on('line', (line) => {
 if (line) {
  logsArr.push(line.toString());
 }
 }).on('close', () => {
 for (var i = 0; i < logsArr.length; i++) {
  // Print the data for each row
  console.log(`Original data: \n ${logsArr[i]}`);
 }
 });
}

总结

以上所述是小编给大家介绍的Nodejs监听日志文件的变化的过程解析,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

NodeJs 相关文章推荐
如何正确使用Nodejs 的 c++ module 链接到 OpenSSL
Aug 03 NodeJs
基于NodeJS的前后端分离的思考与实践(五)多终端适配
Sep 26 NodeJs
nodejs教程之环境安装及运行
Nov 21 NodeJs
在windows上用nodejs搭建静态文件服务器的简单方法
Aug 11 NodeJs
利用nodejs监控文件变化并使用sftp上传到服务器
Feb 18 NodeJs
NodeJS仿WebApi路由示例
Feb 28 NodeJs
nodejs中全局变量的实例解析
Mar 07 NodeJs
nodejs入门教程六:express模块用法示例
Apr 24 NodeJs
详解nodejs实现本地上传图片并预览功能(express4.0+)
Jun 28 NodeJs
详解nodejs中express搭建权限管理系统
Sep 15 NodeJs
Nodejs实现文件上传的示例代码
Sep 26 NodeJs
Nodejs实现用户注册功能
Apr 14 NodeJs
nodejs对项目下所有空文件夹创建gitkeep的方法
Aug 02 #NodeJs
nodejs读取图片返回给浏览器显示
Jul 25 #NodeJs
关于NodeJS中的循环引用详解
Jul 23 #NodeJs
typescript nodejs 依赖注入实现方法代码详解
Jul 21 #NodeJs
nodejs 递归拷贝、读取目录下所有文件和目录
Jul 18 #NodeJs
nodejs二进制与Buffer的介绍与使用
Jul 11 #NodeJs
nodejs中各种加密算法的实现详解
Jul 11 #NodeJs
You might like
多文件上传的例子
2006/10/09 PHP
《PHP边学边教》(04.编写简易的通讯录――视频教程1)
2006/12/13 PHP
php实现的美国50个州选择列表实例
2015/04/20 PHP
PHP6连接SQLServer2005的三部曲
2016/04/15 PHP
Joomla简单判断用户是否登录的方法
2016/05/04 PHP
php并发加锁示例
2016/10/17 PHP
php PDO实现的事务回滚示例
2017/03/23 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
PHP设计模式之迭代器模式Iterator实例分析【对象行为型】
2020/04/26 PHP
Nigma vs Liquid BO3 第二场2.14
2021/03/10 DOTA
javascript parseInt 大改造
2009/09/27 Javascript
date.parse在IE和FF中的区别
2010/07/29 Javascript
原生javascript实现Tab选项卡切换功能
2015/01/12 Javascript
JavaScript中反正弦函数Math.asin()的使用简介
2015/06/14 Javascript
js 判断一组日期是否是连续的简单实例
2016/07/11 Javascript
bootstrap Table插件使用demo
2017/08/07 Javascript
js实现简易聊天对话框
2017/08/17 Javascript
angularjs实现时间轴效果的示例代码
2017/11/29 Javascript
webpack打包js文件及部署的实现方法
2017/12/18 Javascript
Angular开发实践之服务端渲染
2018/03/29 Javascript
使用jquery模拟a标签的click事件无法实现跳转的解决
2018/12/04 jQuery
微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】
2019/02/20 Javascript
详解微信小程序scroll-view横向滚动的实践踩坑及隐藏其滚动条的实现
2019/03/14 Javascript
ES6 class的应用实例分析
2019/06/27 Javascript
layer扩展打开/关闭动画的方法
2019/09/23 Javascript
Python实现字典(dict)的迭代操作示例
2018/06/05 Python
详解python列表(list)的使用技巧及高级操作
2019/08/15 Python
pygame实现俄罗斯方块游戏(AI篇1)
2019/10/29 Python
Python实现隐马尔可夫模型的前向后向算法的示例代码
2019/12/31 Python
python求解汉诺塔游戏
2020/07/09 Python
18-35岁旅游团的全球领导者:Contiki
2017/02/08 全球购物
巴西补充剂和维生素购物网站:Natue
2019/06/17 全球购物
美国翻新电子产品商店:The Store
2019/10/08 全球购物
Jones New York官网:美国女装品牌,受白领女性欢迎
2019/11/26 全球购物
Vue过滤器(filter)实现及应用场景详解
2021/06/15 Vue.js
vue中的可拖拽宽度div的实现示例
2022/04/08 Vue.js