视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Node.js和MongoDB实现简单日志分析系统_node.js
2020-11-27 21:37:30 责编:小采
文档
 在最近的项目中,为了便于分析把项目的日志都存成了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页面,用来作为图形显示的容器:
代码如下:




Rendezvous Monitor System

















几乎是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('');


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();
});

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

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

三、使用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真的是个好东西。

下载本文
显示全文
专题