首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

运用Node.js + MongoDB实现一个简单的日志分析系统

2012-09-10 
使用Node.js + MongoDB实现一个简单的日志分析系统?分类:?NoSQL?MongoDB?JavaScript?Web开发2011-10-31 16

使用Node.js + MongoDB实现一个简单的日志分析系统

?

分类:?NoSQL?MongoDB?JavaScript?Web开发2011-10-31 16:51?4937人阅读?评论(5)?收藏?举报


? ? ? ? 在最近的项目中,为了便于分析把项目的日志都存成了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:

?

[javascript]?view plaincopyprint?
  1. HOST?=?null;?//?localhost??
  2. PORT?=?8001;??
  3. ??
  4. var?fu?=?require("./fu"),??
  5. ????sys?=?require("util"),??
  6. ????url?=?require("url"),??
  7. ????mongo?=?require("./request_handler");??
  8. ??
  9. fu.listen(Number(process.env.PORT?||?PORT),?HOST);??
  10. ??
  11. fu.get("/",?fu.staticHandler("index.html"));??

? ? ? ? ?太简单了吧?!不过的确是这样,一个服务器已经建立起来了。

?

? ? ? ? ?下面看处理请求的request_handler.js代码:

?

[javascript]?view plaincopyprint?
  1. var?mongodb?=?require("mongodb");??
  2. var?fu?=?require("./fu");??
  3. ??
  4. ??
  5. //?TOP?10?user?Action??
  6. fu.get("/userActionTop10",?function(req,?res){??
  7. ??mongodb.connect('mongodb://localhost:27017/log',?function(err,?conn){??
  8. ????conn.collection('action_count',?function(err,?coll){??
  9. ??????coll.find({"value.action":{$in:user_action}}).sort({"value.count":-1}).limit(10).toArray(function(err,?docs){??
  10. ????????if(!err){??
  11. ??????????var?action?=?[];??
  12. ??????????var?count?=?[];??
  13. ??????????for(var?i?=?0;?i?<?docs.length;?i?++){??
  14. ????????????//console.log(docs[i]);??
  15. ????????????action.push(docs[i].value.action);??
  16. ????????????count.push(docs[i].value.count);??
  17. ??????????}??
  18. ??????????res.simpleJSON(200,?{action:action,?count:count});??
  19. ???????????
  20. ??????????//?一定要记得关闭数据库连接??
  21. ??????????conn.close();??
  22. ????????}??
  23. ??????});??
  24. ????});??
  25. ??});??
  26. });??

?

? ? ? ? ? 同样很简单。

?

? ? ? ? ??二、客户端

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

?

[html]?view plaincopyprint?
  1. <!DOCTYPE?html>??
  2. <html>??
  3. ??<head>??
  4. ????<meta?charset="utf-8">??
  5. ????<title>Rendezvous?Monitor?System</title>??
  6. ????<!--[if?lt?IE?9]><script?src="js/excanvas.js"><![endif]-->??
  7. ????<script?src="js/jquery.min.js"></script>??
  8. ????<script?src="js/jquery.jqplot.min.js"></script>??
  9. ????<script?src="js/plugins/jqplot.barRenderer.min.js"></script>??
  10. ????<script?src="js/plugins/jqplot.categoryAxisRenderer.min.js"></script>??
  11. ????<script?src="js/plugins/jqplot.canvasTextRenderer.min.js"></script>??
  12. ????<script?src="js/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>??
  13. ????<script?src="js/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>??
  14. ????<script?src="js/plugins/jqplot.pointLabels.min.js"></script>??
  15. ????<script?src="js/plugins/jqplot.dateAxisRenderer.min.js"></script>??
  16. ????<script?src="js/plugins/jqplot.json2.min.js"></script>??
  17. ????<link?rel="stylesheet"?href="js/jquery.jqplot.min.css">??
  18. ????<link?rel="stylesheet"?href="style/base.css">??
  19. ????<script?src="js/charts.js"></script>??
  20. ??</head>??
  21. ??<body>??
  22. ??</body>??
  23. </html>??

? ? ? ? ? 几乎是jqPlot的示例中的完整拷贝,好吧,我承认我太懒了。

?

? ? ? ? ? 下面是看用来显示生成图形的chart.js:

?

[javascript]?view plaincopyprint?
  1. //?Store?all?chart?drawing?function,?if?we?want?to?disable?one?chart,?only?need??
  2. //?comment?the?push?line?when?putting?fucntion?into?the?array.??
  3. var?draws?=?[];??
  4. ??
  5. /******************************?TOP?10?User?Action?Start?*********************************/??
  6. document.write('<div?id="userActionTop10Chart"></div>');??
  7. ??
  8. ??
  9. var?drawUserActionTop10Chart?=?function(){??
  10. ??if(!$("#userActionTop10Chart").attr('class')){??
  11. ????$("#userActionTop10Chart").attr('class',?'small_chart');??
  12. ??}??
  13. ??
  14. ??
  15. ??$.ajax({??
  16. ????async:false,??
  17. ????url:?'/userActionTop10',??
  18. ????dataType:'json',??
  19. ????cache:?false,??
  20. ????success:function(data){??
  21. ??????try{??
  22. ????????$('#userActionTop10Chart').html('');??
  23. ??
  24. ??
  25. ????????$.jqplot('userActionTop10Chart',?[data.count],?{??
  26. ??????????title:?"TOP?10?User?Action",??
  27. ??????????seriesDefaults:{??
  28. ????????????renderer:$.jqplot.BarRenderer,??
  29. ????????????rendererOptions:?{fillToZero:?true},??
  30. ????????????pointLabels:?{??
  31. ??????????????show:true,??
  32. ??????????????ypadding:1??
  33. ????????????}??
  34. ??????????},??
  35. ??????????axesDefaults:{??
  36. ????????????tickRenderer:$.jqplot.CanvasAxisTickRenderer,??
  37. ????????????tickOptions:?{??
  38. ??????????????angle:?-30,??
  39. ??????????????fontSize:?'12px'??
  40. ????????????}??
  41. ??????????},??
  42. ??????????axes:?{??
  43. ????????????xaxis:?{??
  44. ??????????????renderer:?$.jqplot.CategoryAxisRenderer,??
  45. ??????????????ticks:?data.action??
  46. ????????????},??
  47. ????????????yaxis:?{??
  48. ??????????????pad:?1.05??
  49. ????????????}??
  50. ??????????}??
  51. ????????});??
  52. ??????}catch(e){??
  53. ????????//alert(e.message);??
  54. ??????}??
  55. ????}??
  56. ??});??
  57. }??
  58. ??
  59. ??
  60. draws.push('drawUserActionTop10Chart');??
  61. ??
  62. ??
  63. /*******************************?TOP?10?User?Action?End?************************************/??
  64. ??
  65. /***********?Chart?Start?*****************/??
  66. ??
  67. ??
  68. //Put?your?chart?drawing?function?here??
  69. //1.?insert?a?div?for?the?chart??
  70. //2.?implement?the?function?drawing?chart??
  71. //3.?push?the?function?name?into?the?array?draws??
  72. ??
  73. ??
  74. /***********?Chart?End?*******************/??
  75. ??
  76. ??
  77. ??
  78. //?Draw?all?charts??
  79. var?drawAllCharts?=?function(){??
  80. ??for(var?i?=?0;?i?<?draws.length;?i?++){??
  81. ????eval(draws[i]?+?"()");??
  82. ??}??
  83. ??
  84. ??
  85. ?//Recall?itself?in?5?minute.??
  86. ?window.setTimeout(drawAllCharts,?5?*?60?*?1000);??
  87. }??
  88. ??
  89. ??
  90. //??
  91. $(function(){??
  92. ??drawAllCharts();??
  93. });??

?

?

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

运用Node.js + MongoDB实现一个简单的日志分析系统

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

?

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

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

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

?

[javascript]?view plaincopyprint?
  1. /**************?The?file?is?executed?per?5?minutes?by?/etc/crontab.*****************/??
  2. var?action_count_map?=?function(){??
  3. ??emit(this.action,?{action:this.action,?count:1});??
  4. }??
  5. ??
  6. var?action_count_reduce?=?function(key,?values){??
  7. ??var?count?=?0;??
  8. ??values.forEach(function(value){??
  9. ????count?+=?value.count;??
  10. ??});??
  11. ??return?{action:key,?count?:?count};??
  12. }??
  13. ??
  14. ??
  15. db.log.mapReduce(action_count_map,?action_count_reduce,?{query?:?{'action_count'?:?{$ne:1}},out:?{reduce:'action_count'}});??
  16. ??
  17. 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:

热点排行