Node.js和MongoDB实现简单日志分析系统


Posted in Javascript onApril 25, 2015

在最近的项目中,为了便于分析把项目的日志都存成了JSON格式。之前日志直接存在了文件中,而MongoDB适时闯入了我的视线,于是就把log存进了MongoDB中。log只存起来是没有意义的,最关键的是要从日志中发现业务的趋势、系统的性能漏洞等。之前有一个用Java写的分析模块,运行在Tomcat下。实现相当的重量级,添加一个新指标的流程也比较繁琐,而且由于NFS的原因还导致分析失败。一直想改写,最初想用Ruby On Rails,可是一直没有时间学习和开发(在找借口啊!)。在杭州QCon 2011上又遇到了Node.js,虽然之前也听说过,但是没有深入研究,听了淘宝苏千 的演讲后,当时了就有要用Node.js实现这个日志分析系统的想法。前端用JS,服务器用JS,就连数据库的Shell都是JS,想想就够酷的——当然最关键是代码量小。

一、用Node.js实现服务器端代码

为了有良好的风格和快速的代码编写,不可避免地应该采用一个简单的框架。Express实现了大部分的功能,可是好需要花一定时间熟悉,并且看起来对这个项目来说有些重量级。在Node.js的官网上有一个聊天的Demo ,这个代码简单移动,封装了对URL的处理和返回JSON。于是我就直接使用了fu.js,重写了server.js:

HOST = null; // localhost

PORT = 8001;
var fu = require("./fu"),

    sys = require("util"),

    url = require("url"),

    mongo = require("./request_handler");
fu.listen(Number(process.env.PORT || PORT), HOST);
fu.get("/", fu.staticHandler("index.html"));

太简单了吧?!不过的确是这样,一个服务器已经建立起来了。
下面看处理请求的request_handler.js代码:

var mongodb = require("mongodb");

var fu = require("./fu");


// TOP 10 user Action

fu.get("/userActionTop10", function(req, res){

  mongodb.connect('mongodb://localhost:27017/log', function(err, conn){

    conn.collection('action_count', function(err, coll){

      coll.find({"value.action":{$in:user_action}}).sort({"value.count":-1}).limit(10).toArray(function(err, docs){

        if(!err){

          var action = [];

          var count = [];

          for(var i = 0; i < docs.length; i ++){

            //console.log(docs[i]);

            action.push(docs[i].value.action);

            count.push(docs[i].value.count);

          }

          res.simpleJSON(200, {action:action, count:count});

         

          // 一定要记得关闭数据库连接

          conn.close();

        }

      });

    });

  });

});

二、客户端

日志系统的最重要的是可视化显示,这里使用了JQuery的一个插件jqPlot Chart 。首先使用一个静态的HTML页面,用来作为图形显示的容器:

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>Rendezvous Monitor System</title>

    <!--[if lt IE 9]><script src="js/excanvas.js"><![endif]-->

    <script src="js/jquery.min.js"></script>

    <script src="js/jquery.jqplot.min.js"></script>

    <script src="js/plugins/jqplot.barRenderer.min.js"></script>

    <script src="js/plugins/jqplot.categoryAxisRenderer.min.js"></script>

    <script src="js/plugins/jqplot.canvasTextRenderer.min.js"></script>

    <script src="js/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>

    <script src="js/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>

    <script src="js/plugins/jqplot.pointLabels.min.js"></script>

    <script src="js/plugins/jqplot.dateAxisRenderer.min.js"></script>

    <script src="js/plugins/jqplot.json2.min.js"></script>

    <link rel="stylesheet" href="js/jquery.jqplot.min.css">

    <link rel="stylesheet" href="style/base.css">

    <script src="js/charts.js"></script>

  </head>

  <body>

  </body>

</html>

几乎是jqPlot的示例中的完整拷贝,好吧,我承认我太懒了。
下面是看用来显示生成图形的chart.js:

// Store all chart drawing function, if we want to disable one chart, only need

// comment the push line when putting fucntion into the array.

var draws = [];
/****************************** TOP 10 User Action Start *********************************/

document.write('<div id="userActionTop10Chart"></div>');


var drawUserActionTop10Chart = function(){

  if(!$("#userActionTop10Chart").attr('class')){

    $("#userActionTop10Chart").attr('class', 'small_chart');

  }


  $.ajax({

    async:false,

    url: '/userActionTop10',

    dataType:'json',

    cache: false,

    success:function(data){

      try{

        $('#userActionTop10Chart').html('');


        $.jqplot('userActionTop10Chart', [data.count], {

          title: "TOP 10 User Action",

          seriesDefaults:{

            renderer:$.jqplot.BarRenderer,

            rendererOptions: {fillToZero: true},

            pointLabels: {

              show:true,

              ypadding:1

            }

          },

          axesDefaults:{

            tickRenderer:$.jqplot.CanvasAxisTickRenderer,

            tickOptions: {

              angle: -30,

              fontSize: '12px'

            }

          },

          axes: {

            xaxis: {

              renderer: $.jqplot.CategoryAxisRenderer,

              ticks: data.action

            },

            yaxis: {

              pad: 1.05

            }

          }

        });

      }catch(e){

        //alert(e.message);

      }

    }

  });

}


draws.push('drawUserActionTop10Chart');


/******************************* TOP 10 User Action End ************************************/
/*********** Chart Start *****************/


//Put your chart drawing function here

//1. insert a div for the chart

//2. implement the function drawing chart

//3. push the function name into the array draws


/*********** Chart End *******************/

// Draw all charts

var drawAllCharts = function(){

  for(var i = 0; i < draws.length; i ++){

    eval(draws[i] + "()");

  }


 //Recall itself in 5 minute.

 window.setTimeout(drawAllCharts, 5 * 60 * 1000);

}


//

$(function(){

  drawAllCharts();

});

服务器端和客户端的代码都有了,那就跑起来看效果吧:

Node.js和MongoDB实现简单日志分析系统

好像忘了什么?日志的分析代码。

三、使用MongoDB 增量式MapReduce实现日志分析

在MongoDB的文档中有关于Incremental MapReduce的介绍。刚开始一直以为MongoDB实现Streaming处理,可以自动执行增量式的MapReduce。最后发现原来是我理解有误,文档里并没有写这一点,只是说明了如何设置才能增量执行MapReduce。

为了方便,我把MapReduce使用MongoDB的JavaScript写在了单独的js文件中,然后通过crontab定时执行。stats.js的代码:

/************** The file is executed per 5 minutes by /etc/crontab.*****************/

var action_count_map = function(){

  emit(this.action, {action:this.action, count:1});

}
var action_count_reduce = function(key, values){

  var count = 0;

  values.forEach(function(value){

    count += value.count;

  });

  return {action:key, count : count};

}


db.log.mapReduce(action_count_map, action_count_reduce, {query : {'action_count' : {$ne:1}},out: {reduce:'action_count'}});
db.log.update({'action_count':{$ne:1}}, {$set:{'action_count':1}}, false, true);

 思路很简单:
1. 在map中将每个action访问次数设为1
2. reduce中,统计相同action的访问次数
3. 执行mapReduce。指定了查询为‘action_count'不等于1,也就是没有执行过该统计;将结果存储在‘action_count'集合,并且使用reduce选项表示该结果集作为下次reduce的输入。
4. 在当前所有日志记录设置'action_count'的值为1,表示已经执行过该统计。不知道这种是否会造成没有还没有统计过的记录也被更新??望有经验的大侠赐教!

定时执行stats.js的shell:

*/5 * * * * root cd /root/log; mongo localhost:27017/log stats.js

好了,这就是全部的代码,没有什么特别玄妙的地方,不过Node.js真的是个好东西。

Javascript 相关文章推荐
JSON 入门指南 想了解json的朋友可以看下
Aug 26 Javascript
jQuery 第二课 操作包装集元素代码
Mar 14 Javascript
JS getStyle获取最终样式函数代码
Apr 01 Javascript
jQuery1.4.2与老版本json格式兼容的解决方法
Feb 12 Javascript
jQuery学习笔记之jQuery+CSS3的浏览器兼容性
Jan 19 Javascript
js实现YouKu的漂亮搜索框效果
Aug 19 Javascript
基于jQuery Bar Indicator 插件实现进度条展示效果
Sep 30 Javascript
用JavaScript判断CSS浏览器类型前缀的两种方法
Oct 08 Javascript
JS解决iframe之间通信和自适应高度的问题
Aug 24 Javascript
详解Vue 动态添加模板的几种方法
Apr 25 Javascript
深入浅析angular和vue还有jquery的区别
Aug 13 jQuery
Layui数据表格跳转到指定页的实现方法
Sep 05 Javascript
node.js操作mongodb学习小结
Apr 25 #Javascript
JavaScript按值删除数组元素的方法
Apr 24 #Javascript
JavaScript获取一个范围内日期的方法
Apr 24 #Javascript
jQuery中next方法用法实例
Apr 24 #Javascript
JavaScript实现多个重叠层点击切换效果的方法
Apr 24 #Javascript
javascript实现的右下角弹窗实例
Apr 24 #Javascript
js上传图片及预览功能实例分析
Apr 24 #Javascript
You might like
php去除字符串换行符示例分享
2014/02/13 PHP
php中$美元符号与Zen Coding冲突问题解决方法分享
2014/05/28 PHP
php调用shell的方法
2014/11/05 PHP
thinkphp循环结构用法实例
2014/11/24 PHP
实例详解PHP中html word 互转的方法
2016/01/28 PHP
phalcon框架使用指南
2016/02/23 PHP
php如何比较两个浮点数是否相等详解
2019/02/12 PHP
PHP数组实际占用内存大小原理解析
2020/12/11 PHP
Gambit vs CL BO3 第一场 2.13
2021/03/10 DOTA
javascript编程起步(第六课)
2007/02/27 Javascript
Mootools 1.2教程 Tooltips
2009/09/15 Javascript
JavaScript 闭包在封装函数时的简单分析
2009/11/28 Javascript
jquery 结合C#后台的数组对文章的关键字自动添加链接的代码
2011/07/15 Javascript
jBox 2.3基于jquery的最新多功能对话框插件 常见使用问题解答
2011/11/10 Javascript
jQuery+slidereveal实现的面板滑动侧边展出效果
2015/03/14 Javascript
javascript常见数字进制转换实例分析
2016/04/21 Javascript
原生JS和jQuery操作DOM对比总结
2017/01/19 Javascript
Three.js开发实现3D地图的实践过程总结
2017/11/20 Javascript
Node.js实现用户评论社区功能(体验前后端开发的乐趣)
2019/05/09 Javascript
python打开网页和暂停实例
2014/09/30 Python
Python入门篇之文件
2014/10/20 Python
Python列表append和+的区别浅析
2015/02/02 Python
Python 判断 有向图 是否有环的实例讲解
2018/02/01 Python
python numpy 按行归一化的实例
2019/01/21 Python
详解Python中的测试工具
2019/06/09 Python
详解Python利用random生成一个列表内的随机数
2019/08/21 Python
使用Pyhton集合set()实现成果查漏的例子
2019/11/24 Python
基于python实现语音录入识别代码实例
2020/01/17 Python
Python dict和defaultdict使用实例解析
2020/03/12 Python
Python pip安装模块提示错误解决方案
2020/05/22 Python
Python:__eq__和__str__函数的使用示例
2020/09/26 Python
Django多个app urls配置代码实例
2020/11/26 Python
Python读写锁实现实现代码解析
2020/11/28 Python
python3通过subprocess模块调用脚本并和脚本交互的操作
2020/12/05 Python
2014年大班保育员工作总结
2014/12/02 职场文书
浅谈tf.train.Saver()与tf.train.import_meta_graph的要点
2021/05/26 Python