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 相关文章推荐
简单的JS多重继承示例
Mar 13 Javascript
js输出列表实现代码
Sep 12 Javascript
手把手教你自己写一个js表单验证框架的方法
Sep 14 Javascript
javascript中的事件代理初探
Mar 08 Javascript
javascript与css3动画结合使用小结
Mar 11 Javascript
JavaScript中模拟实现jsonp
Jun 19 Javascript
AngularJS 中使用Swiper制作滚动图不能滑动的解决方法
Nov 15 Javascript
vue 多入口文件搭建 vue多页面搭建的实例讲解
Mar 12 Javascript
vue代理和跨域问题的解决
Jul 18 Javascript
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
Mar 06 Javascript
微信小程序使用echarts获取数据并生成折线图
Oct 16 Javascript
TypeScript 运行时类型检查补充工具
Sep 28 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+mysql来做一个功能强大的在线计算器
2010/10/12 PHP
VIM中设置php自动缩进为4个空格的方法详解
2013/06/14 PHP
解析php做推送服务端实现ios消息推送
2013/07/01 PHP
php生成PDF格式文件并且加密
2015/06/22 PHP
php 广告点击统计代码(php+mysql)
2018/02/21 PHP
数理公式,也可以这么唯美
2021/03/10 无线电
JavaScript实现点击自动选择TextArea文本的方法
2015/07/02 Javascript
JS实现无限级网页折叠菜单(类似树形菜单)效果代码
2015/09/17 Javascript
深入理解bootstrap框架之入门准备
2016/10/09 Javascript
详解获取jq ul第一个li定位的四种解决方案
2016/11/23 Javascript
Nodejs 发送Post请求功能(发短信验证码例子)
2017/02/09 NodeJs
jQuery实现弹窗居中效果类似alert()
2017/02/27 Javascript
react配合antd组件实现的管理系统示例代码
2018/04/24 Javascript
AngularJS自定义过滤器用法经典实例总结
2018/05/17 Javascript
JS实现方形抽奖效果
2018/08/27 Javascript
详解react内联样式使用webpack将px转rem
2018/09/13 Javascript
js脚本中执行java后台代码方法解析
2019/10/11 Javascript
js正则匹配多个全部数据问题
2019/12/20 Javascript
JS实现图片懒加载(lazyload)过程详解
2020/04/02 Javascript
python 文件和路径操作函数小结
2009/11/23 Python
跟老齐学Python之总结参数的传递
2014/10/10 Python
Python中给List添加元素的4种方法分享
2014/11/28 Python
Python数据结构与算法之完全树与最小堆实例
2017/12/13 Python
Django异步任务之Celery的基本使用
2019/03/23 Python
33个Python爬虫项目实战(推荐)
2019/07/08 Python
Python numpy线性代数用法实例解析
2019/11/15 Python
Python sys模块常用方法解析
2020/02/20 Python
Python Charles抓包配置实现流程图解
2020/09/29 Python
浅谈移动端网页图片预加载方案
2018/11/05 HTML / CSS
小米官方旗舰店:Xiaomi
2020/08/07 全球购物
面向对象编程是如何提高软件开发水平的
2014/05/06 面试题
审计局2014法制宣传日活动总结
2014/11/01 职场文书
2015暑假实习报告范文
2015/07/13 职场文书
早恋主题班会
2015/08/14 职场文书
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
2022/04/20 MySQL
Java 异步任务计算FutureTask
2022/04/28 Java/Android