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 相关文章推荐
javascript 实现子父窗体互相传值的简单实例
Feb 17 Javascript
浅谈JavaScript超时调用和间歇调用
Aug 30 Javascript
jQuery Real Person验证码插件防止表单自动提交
Nov 06 Javascript
基于jquery实现弹幕效果
Sep 29 Javascript
jQuery.parseHTML() 函数详解
Jan 09 Javascript
微信小程序之picker日期和时间选择器
Feb 09 Javascript
详解angular中通过$location获取路径(参数)的写法
Mar 21 Javascript
js构建二叉树进行数值数组的去重与优化详解
Mar 26 Javascript
vue.js使用v-pre与v-html输出HTML操作示例
Jul 07 Javascript
JavaScript设计模式之装饰者模式定义与应用示例
Jul 25 Javascript
Vue文件配置全局变量的实例
Sep 06 Javascript
vue递归组件实战之简单树形控件实例代码
Aug 27 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
把从SQL中取出的数据转化成XMl格式
2006/10/09 PHP
一个用php3编写的简单计数器
2006/10/09 PHP
解析php入库和出库
2013/06/25 PHP
js如何取消事件冒泡
2013/09/23 Javascript
Javascript中判断对象是否为空
2015/06/10 Javascript
JavaScript创建闭包的两种方式的优劣与区别分析
2015/06/22 Javascript
用javascript实现自动输出网页文本
2015/07/30 Javascript
jquery拖拽效果完整实例(附demo源码下载)
2016/01/14 Javascript
学习vue.js条件渲染
2016/12/03 Javascript
JavaScript 栈的详解及实例代码
2017/01/22 Javascript
ES6 javascript的异步操作实例详解
2017/10/30 Javascript
基于Swiper实现移动端页面图片轮播效果
2017/12/28 Javascript
Vue页面骨架屏的实现方法
2018/05/22 Javascript
JS实现图片旋转动画效果封装与使用示例
2018/07/09 Javascript
深入理解JavaScript 中的执行上下文和执行栈
2018/10/23 Javascript
elementUI 设置input的只读或禁用的方法
2018/10/30 Javascript
小程序实现页面顶部选项卡效果
2018/11/06 Javascript
详解JS判断页面是在手机端还是在PC端打开的方法
2019/04/26 Javascript
微信小程序移动拖拽视图-movable-view实例详解
2019/08/17 Javascript
vue基于v-charts封装双向条形图的实现代码
2019/12/09 Javascript
jquery选择器和属性对象的操作实例分析
2020/01/10 jQuery
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
Python操作MySQL数据库的方法
2018/06/20 Python
Django模型修改及数据迁移实现解析
2019/08/01 Python
PyCharm 2020.2 安装详细教程
2020/09/25 Python
HTML5不支持标签和新增标签详解
2016/06/27 HTML / CSS
AmazeUI的JS表单验证框架实战示例分享
2020/08/21 HTML / CSS
耐克巴西官方网站:Nike巴西
2016/08/14 全球购物
澳大利亚波西米亚风连衣裙在线商店:Fortunate One
2019/04/01 全球购物
2015年双拥工作总结
2015/04/08 职场文书
党员转正介绍人意见
2015/06/03 职场文书
培训简讯范文
2015/07/20 职场文书
学风建设主题班会
2015/08/17 职场文书
体育委员竞选稿
2015/11/21 职场文书
详解MySQL中timestamp和datetime时区问题导致做DTS遇到的坑
2021/12/06 MySQL
Python几种酷炫的进度条的方式
2022/04/11 Python