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的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践
Sep 26 NodeJs
nodejs开发微博实例
Mar 25 NodeJs
实例详解Nodejs 保存 payload 发送过来的文件
Jan 14 NodeJs
NodeJS学习笔记之Module的简介
Mar 24 NodeJs
nodejs个人博客开发第三步 载入页面
Apr 12 NodeJs
Linux Centos7.2下安装nodejs&amp;npm配置全局路径的教程
May 15 NodeJs
nodejs微信开发之自动回复的实现
Mar 17 NodeJs
nodejs中实现修改用户路由功能
May 24 NodeJs
nodejs log4js 使用详解
May 31 NodeJs
nodejs提示:cross-device link not permitted, rename错误的解决方法
Jun 10 NodeJs
windows如何把已安装的nodejs高版本降级为低版本(图文教程)
Dec 14 NodeJs
nodejs中的异步编程知识点详解
Jan 17 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
php文件怎么打开 如何执行php文件
2011/12/21 PHP
PHP使用星号替代用户名手机和邮箱的实现代码
2018/02/07 PHP
ThinkPHP 3.2.3实现加减乘除图片验证码
2018/12/05 PHP
让JavaScript 轻松支持函数重载 (Part 1 - 设计)
2009/08/04 Javascript
jqGrid jQuery 表格插件测试代码
2011/08/23 Javascript
javascript阻止scroll事件多次执行的思路及实现
2013/11/08 Javascript
jQuery实现HTML5 placeholder效果实例
2014/12/09 Javascript
JS实现文件动态顺序载入的方法
2015/03/07 Javascript
jQuery实现当前页面标签高亮显示的方法
2015/03/10 Javascript
简介JavaScript中toUpperCase()方法的使用
2015/06/06 Javascript
JS基于cookie实现来宾统计记录访客信息的方法
2015/08/04 Javascript
javascript常用的方法整理
2015/08/20 Javascript
jquery插件tytabs.jquery.min.js实现渐变TAB选项卡效果
2015/08/25 Javascript
使用json来定义函数,在里面可以定义多个函数的实现方法
2016/10/28 Javascript
bootstrap输入框组件使用方法详解
2017/01/19 Javascript
ES6入门教程之Class和Module详解
2017/05/17 Javascript
关于vue面试题汇总
2018/03/20 Javascript
Vue之mixin全局的用法详解
2018/08/22 Javascript
[00:33]2018DOTA2亚洲邀请赛TNC出场
2018/04/04 DOTA
收集的几个Python小技巧分享
2014/11/22 Python
Linux中安装Python的交互式解释器IPython的教程
2016/06/13 Python
使用Python更换外网IP的方法
2018/07/09 Python
Python 实现两个列表里元素对应相乘的方法
2018/11/14 Python
Python eval的常见错误封装及利用原理详解
2019/03/26 Python
python使用KNN算法识别手写数字
2019/04/25 Python
Python中Flask-RESTful编写API接口(小白入门)
2019/12/11 Python
python自动脚本的pyautogui入门学习
2020/04/01 Python
大学新闻系自荐书
2014/05/31 职场文书
搞笑婚庆主持词
2015/06/29 职场文书
2016年寒假见闻
2015/10/10 职场文书
2016年学习雷锋精神广播稿
2015/12/17 职场文书
《7的乘法口诀》教学反思
2016/02/18 职场文书
同学会演讲稿
2019/04/02 职场文书
为什么阅读对所有年龄段的孩子都很重要?
2019/07/08 职场文书
 python中的元类metaclass详情
2022/05/30 Python
SpringBoot项目部署到阿里云服务器的实现步骤
2022/06/28 Java/Android