NodeJS读取分析Nginx错误日志的方法


Posted in NodeJs onMay 14, 2019

网上很少看到有用NodeJS运维系列文章,后续我会更新一些NodeJS运维相关的内容又或者说让我们更加的深入了解一些服务器的知识以及自动化运维方面的基础知识 为什么要做错误日志分析,因为网上这方面的工具不多我找到一个goaccess但是都是分析成功日志以及用户访问趋势,找了半天没找着自己想要的索性就自己利用Node造一个

错误日志分析

首先我们要读取Nginx日志,我们可以看到Nginx的错误日志格式一般都是这样子,需要注意的是Nginx的错误日志格式是差不多的因为无法设置日志格式只能设置日志错误等级所以我们分析的时候很方便

NodeJS读取分析Nginx错误日志的方法

 这里我们用到readline

逐行读取,简单来说可以做

  • 文件逐行读取:比如说进行日志分析。
  • 自动完成:比如输入npm,自动提示"help init install"。
  • 命令行工具:比如npm init这种问答式的脚手架工具。 这里我们主要做日志分析其他的感兴趣可以琢磨一下

实现方法

const readline = require('readline');
const fs = require('fs');
const path = require('path');
console.time('readline-time')
const rl = readline.createInterface({
 input: fs.createReadStream(path.join(__dirname, '../public/api.err.log'), {
  start: 0,
  end: Infinity
 }),

});
let count = 0; 
rl.on('line', (line) => {
 const arr = line.split(', ');
 const time = arr[0].split('*')[0].split('[')[0].replace(/\//g, '-');//获取到时间
 const error = arr[0].split('*')[1].split(/\d\s/)[1];//错误原因
 const client = arr[1].split(' ')[1];//请求的客户端
 const server = arr[2].split(' ')[1];//请求的网址
 const url = arr[3].match(/\s\/(\S*)\s/)[0].trim()//获取请求链接
 const upstream = arr[4].match(/(?<=").*?(?=")/g)[0];//获取上游
 const host = arr[5].match(/(?<=").*?(?=")/g)[0];//获取host
 const referrer = arr[6] ? arr[6].match(/(?<=").*?(?=")/g)[0] : '';//来源
 console.log(`时间:${time}-原因:${error}-客户端:${client}-网址:${server}-地址:${url}-上游:${upstream}-主机:${host}-来源:${referrer}`); 
 count++;
});
rl.on('close', () => {
 let size = fs.statSync(path.join(__dirname, '../public/api.err.log')).size;
 console.log(`读取完毕:${count};文件位置:${size % 2 === 0}`);
 console.timeEnd('readline-time')
});

上面代码有几点需要注意的是会创建一个文件可读流然后由于演示所以我是直接找的本地地址如果是生产环境的话大家可以直接填写服务器上的错误日志地址,如果没有Nginx错误日志分割的话每天会产生很多日志,createReadStream读取几十M的文件还好如果读取几百M或者上G的容量日志这会造成性能问题,所以我们需要在每次createReadStream没必要每次从0字节开始读取,ceateReadStream提供了start和end

 NodeJS读取分析Nginx错误日志的方法

所以我们每次可以在读取完之后记录一下当前文件字节大小下一次读取文件就是可以用该文件上次的大小开始读取

let size = fs.statSync(path.join(__dirname, '../public/api.err.log')).size;

我们可以对比一下每次从0字节开始读取和从指定字节读取

NodeJS读取分析Nginx错误日志的方法

保存数据进行分析

这里我是用node-schedule这个库进行定时保存错误日志和linux的cron差不多,用的mongodb保存数据,这里更推荐大家用elasticsearch来做日志分析

rl.on('close', async () => {
     let count = 0;
     for (let i of rlist) {
      count++;
      if (count % 500 === 0) {
       const res = await global.db.collection('logs').bulkWrite(rlist.slice(count, count + 500), { ordered: false, w: 1 }).catch(err => { console.error(`批量插入出错${err}`) }); 
      } else if (count === rlist.length - 1) {
      //批量插入 数据
       const res = await global.db.collection('logs').bulkWrite(rlist.slice(rlist - (rlist % 500), rlist.length), { ordered: false, w: 1 });
       let size = fs.statSync(addres).size;
       size = size % 2 === 0 ? size : size + 1;//保证字节大小是偶数 不然会出现读取上行内容不完整的情况
       count = 0;
       rlist.length = [];
       //更新数据库里面文件的size
       global.db.collection('tasks').updateOne({ _id: addre }, { $set: { _id: addre, size, date: +new Date() } }, { upsert: true }); 
      }
     }
     resolve(true);
    })

上面主要是500条保存一次,因为我用的是批量插入然后mongodb有限制一次性最多插入16M数据的限制,所以大家看自己清空决定一次性插入多少条 犹豫对readline的实现比较感兴趣,就去翻阅了一下源码发现并不是我们想的那么复杂, readline源码 ,下面贴一下line事件的源码,想继续深入的同学可以看看全部的源码

if (typeof s === 'string' && s) {
     var lines = s.split(/\r\n|\n|\r/);
     for (var i = 0, len = lines.length; i < len; i++) {
      if (i > 0) {
       this._line();
      }
      this._insertString(lines[i]);
     }
    }
...
Interface.prototype._line = function() {
 const line = this._addHistory();
 this.clearLine();
 this._onLine(line);
};
...
Interface.prototype._onLine = function(line) {
 if (this._questionCallback) {
  var cb = this._questionCallback;
  this._questionCallback = null;
  this.setPrompt(this._oldPrompt);
  cb(line);
 } else {
  this.emit('line', line);
 }
};

保存的数据需要进行分析比如哪个IP访问最多哪条错误最多可以用聚合来进行分析贴出示例分析某个IP在某一天访问出错最多的原因

db.logs.aggregate(
 // Pipeline
 [
 // Stage 1
 {
  $group: {
   '_id': { 'client': '114.112.163.28', 'server': '$server', 'error': '$error', 'url': '$url', 'upstream': '$upstream','date':'$date' ,'msg':'$msg' } ,
     
   'date':{'$addToSet':'$date'},
   count: { '$sum': 1 } 
  }
 },
 // Stage 2
 {
  $match: { 
     count: { $gte: 1 },
     date: ['2019-05-10']
    
  }
 },
 {
  $sort: {
    count: -1
  }
 },
 ],
 // Options
 {
 cursor: {
  batchSize: 50
 },
 allowDiskUse: true
 }
);

总结

以上所述是小编给大家介绍的NodeJS读取分析Nginx错误日志的方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

NodeJs 相关文章推荐
nodejs教程 安装express及配置app.js文件的详细步骤
May 11 NodeJs
使用nodejs、Python写的一个简易HTTP静态文件服务器
Jul 18 NodeJs
NodeJS整合银联网关支付(DEMO)
Nov 09 NodeJs
搭建简单的nodejs http服务器详解
Mar 09 NodeJs
详解nodejs中的process进程
Mar 19 NodeJs
详解NodeJS框架express的路径映射(路由)功能及控制
Mar 24 NodeJs
NodeJs的fs读写删除移动监听
Apr 28 NodeJs
Nodejs中Express 常用中间件 body-parser 实现解析
May 22 NodeJs
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
Sep 06 NodeJs
nodejs对express中next函数的一些理解
Sep 08 NodeJs
nodejs简单读写excel内容的方法示例
Mar 16 NodeJs
nodejs基于express实现文件上传的方法
Mar 19 NodeJs
nodejs搭建本地服务器并访问文件操作示例
May 11 #NodeJs
M2实现Nodejs项目自动部署的方法步骤
May 05 #NodeJs
nodejs通过钉钉群机器人推送消息的实现代码
May 05 #NodeJs
nodejs中request库使用HTTPS代理的方法
Apr 30 #NodeJs
详解微信小程序-获取用户session_key,openid,unionid - 后端为nodejs
Apr 29 #NodeJs
nodejs检测因特网是否断开的解决方案
Apr 17 #NodeJs
Nodejs实现用户注册功能
Apr 14 #NodeJs
You might like
德劲1104的电路分析与改良
2021/03/01 无线电
php不允许用户提交空表单(php空值判断)
2013/11/12 PHP
使用PHP如何实现高效安全的ftp服务器(一)
2015/12/20 PHP
PHP常见字符串处理函数用法示例【转换,转义,截取,比较,查找,反转,切割】
2016/12/24 PHP
PHP实现下载远程图片保存到本地的方法
2017/06/19 PHP
用php实现分页效果的示例代码
2020/12/10 PHP
jquery中通过父级查找进行定位示例
2013/06/28 Javascript
jquery实现select选中行、列合计示例
2014/04/25 Javascript
js实现仿百度瀑布流的方法
2015/02/05 Javascript
使用jQuery Ajax 请求webservice来实现更简练的Ajax
2016/08/04 Javascript
AngularJS中$apply方法和$watch方法用法总结
2016/12/13 Javascript
jQuery.Validate表单验证插件的使用示例详解
2017/01/04 Javascript
Nodejs中Express 常用中间件 body-parser 实现解析
2017/05/22 NodeJs
vue组件实现文字居中对齐的方法
2017/08/23 Javascript
vue组件的写法汇总
2018/04/12 Javascript
laydate如何根据开始时间或者结束时间限制范围
2018/11/15 Javascript
Linux下编译安装MySQL-Python教程
2015/02/02 Python
python更新列表的方法
2015/07/28 Python
python生成圆形图片的方法
2020/03/25 Python
Python cookbook(数据结构与算法)根据字段将记录分组操作示例
2018/03/19 Python
python实现扫描日志关键字的示例
2018/04/28 Python
Python读写文件基础知识点
2019/06/10 Python
将python安装信息加入注册表的示例
2019/11/20 Python
Python 获取命令行参数内容及参数个数的实例
2019/12/20 Python
Python Pickle 实现在同一个文件中序列化多个对象
2019/12/30 Python
使用python处理题库表格并转化为word形式的实现
2020/04/14 Python
css3实现可拖动的魔方3d效果
2019/05/07 HTML / CSS
简单整理HTML5的基本特性和语法
2016/02/18 HTML / CSS
Smilodox官方运动服装店:从运动服到健身配件
2020/08/27 全球购物
程序运行正确, 但退出时却"core dump"了,怎么回事
2014/02/19 面试题
对象的序列化(serialization)类是面向流的,应如何将对象写入到随机存取文件中
2015/06/22 面试题
环境工程与管理大学毕业生求职信
2013/10/02 职场文书
临床医学专业毕业生的自我评价
2013/10/17 职场文书
骨干教师考核评语
2014/12/31 职场文书
早恋主题班会
2015/08/14 职场文书
为什么MySQL8新特性会修改自增主键属性
2022/04/18 MySQL