eagle-分离后的错误监控方案

前端技术在不断更新迭代,WEB前后端分离已逐渐在行业内得到普及,而且有不少公司已经在使用Nodejs作为前端WebServer。通常情况下,Nodejs在前后端分离中往往起到路由、模板引擎、代理后端接口等作用。所以在此背景下产生的各种服务端错误监控和处理,也显得尤为重要。

##提纲:

  • 1、eagle背景、介绍
    1)前端的资源库
    2)eagle名字来源

  • 2、eagle要解决的问题
    1)前后端工程师沟通问题
    2)前端只能切切页面
    3)量特别大的时候,线上时不时会down掉
    4)浏览器层面的js报错

  • 3、eagle怎么做的
    1)线上记录,发送日志
    2)处理机上接收、分析日志
    3)报警机制
    4)日志的可视化
    5)提醒大家

  • 4、eagle还能做什么

    1)反馈   
    2)在切版本的时候统计信息  
  • 5、eagle给我们带来了什么
    1)几次大促
    2)新同学熟悉node
    3)恶意请求

半个月前,很幸运在w3ctech上参加了一次技术交流,第一次这么多人面前演讲,还是作技术上的分享,略紧张。非常感谢@先烈给的这次机会。

##一、eagle背景

说eagle之前,我们先简单说一下美丽说的前端架构。目前美丽说商业部的技术栈是业界比较流行的第一代前后端分离。后端用php、java等提供纯粹的Restful数据服务,前端则负责数据展现。Node用来接受后端的json数据,交给模板引擎,然后模板引擎渲染。前端环境有个好听的名字,叫hornbill,主要是集成了线上的各种服务,这是由@荣威前辈亲手打造,由@先烈大神加以改进。hornbill主要包括静态文件服务、路由、模板、代理接口、缓存等等;当然还有一个前端工具系统fedev,由@先烈倾力打造,主要包括本地常用命令、部署、文件模板、数据模拟、沙盒服务等等。而eagle有一部分就运行在hornbill里。hornbill负责记录来自模板、接口的错误日志,eagle的logsever部分负责把日志发送到处理机上。

##二、what’s eagle?

eagle是用node做的前端日志处理系统。他可以帮助工程师们及时发现问题,保证线上服务的稳定。

##三、why take eagle?

####1、工程师们无法快速的定位问题
在没有eagle之前,线上一旦出了问题,一般是指qa、pm发现了问题,他们一般会在讨论组里吼,认为是前端的问题。前端工程师们只能ssh到线上去看日志,或者看下具体的接口内容,并逐一分析日志的一些情况,找到可能是哪个接口出错了。然后找到对应的rd同学,进一步找到问题的所在。

如果是在qq上找到rd同学了,往往会有如下的对话:

fe:在?(1 minute or more later…)
rd:嗯。
fe:线上出了个bug,有可能是我们的问题。
rd:嗯,什么问题?
fe:xxx.meilishuo.com/xxx/xxx页面里,有个表单提交不了了,你看看是不是你们接口的问题?(5 minutes or more later. ..)
rd:稍等,我在解另一个bug。
fe:哦,好。(1 hour or more later…)
fe:在?(1 minute or more later…)
….

最终fe们翻看了下php代码,发现他根本就没读那个字段(当然有点极端,这应该是在自测环节就被杜绝的,只是举个例子)。
我们知道,在实际做项目的过程中,开发时间如果是10天的话,其实就有接近3天的时间是在沟通。也就是说我们把很大部分的时间花在了沟通中,尤其是这种前后端分离的比较明显的技术栈——从接口规范的敲定、到联调再到到线上部署,基本上每一个环节都需要fe和rd的沟通,而碰到一些比较不太好说话的rd同学,沟通往往会更费劲。
而且问题出现了,大家并不知道如何去分配人力去解决这个bug。常用的就是leader去指定A解决,A同学发现并不是他写的代码,最后通过询问或者查看svn log,发现是同学B,再找到这个人。
我们在回到快速定位这个问题。既然每次沟通如此费劲,那么我们是不是要找出一个标准的方法,让fe、rd们看一眼就知道问题错在哪儿了。

####2、前端只能切切页面
以前做监控基本上只有后端才能做,通过后端代码里记录的错误日志,实时的分析;或者分析nginx的access日志进行分析,当然nginx的日志在具体排查问题方面可能有点不足。当然这两种方式很好,而且目前在我们的整个架构里也都在用。不过,对于目前这样的多个backend对一个frontend的架构来说,每个后端团队都必须拥有自己的一套错误监控系统,不管是从成本还是从错误监控标准来说都是一个巨大的抢占。
但是由于我们利用了前后端分离,因此我们在前端就能完全拿到后端api的信息,而且这是最及时的,最可靠的。通过这些api,我们可以分析 “响应时间”、“后端提供的错误”等等信息。一旦拿到这些信息后我们就可以尽情发挥,后果不堪设想。

####3、用户有时反馈速度太慢、服务器登录不上等等

我们假设一个情况,后端有台服务器由于并发太高,突然down了,这时候前端就有一定几率命中不了后端数据,这直接导致前端拿不到数据。而这时候将会导致用户看不到数据。若没有一定的监控,我们知道机器down掉的唯一途径将是用户反馈。而让用户等待很久时间当然是不靠谱了。
一些粗心的同学会在没有接受到数据的时候没有做好容错,那页面上一不小心就会报出一个500错误页。这不论对用户还是对程序狗的KPI来说,都是很不乐观的。

####4、前端浏览器js报错
当然还有浏览器端js的报错,这个一般是捕获onerror事件,把错误内容往服务器上传。不过,由于线上的代码都是压缩、合并之后的,所以目前我们还没有找到合适的方法去定位究竟是哪个文件出了问题,只能看到大致的内容。

第二部分小结:
之前线上的前端错误日志只留在的自己的机器上,没有做任何的分析,处理,真正碰到问题了只能到线上去查看,耗时耗力,所以我们思考,何不弄一个自动分析错误来源,并实时报警的机制。

##三、how eagle works?

eagle可以分为三个部分、线上日志收集推送、日志处理、日志报表平台。收集json;处理:不同阈值不同警报、指定人;不同时间维度的报表。

####1、 日志的记录,日志简单分类

把日志大致分为info,error,api,fatal这几种。
info呢,一般只做些记录,没什么特殊含义,一般只记录在服务器上,不往本地传,用做备案。一般会记录一些404等信息。
error呢,一般记录一些不太严重的错误,这个level下可以细分出一些东西,用来自己捣腾,比如反馈、统计什么的。
api呢,专门监听后端接口的错误,比如接口响应时间、权限问题引起的访问等等
fatal呢,一般是模板或者路由的错误。这个一般是非常严重的错,直接影响到页面的可用性。一般出现这样的错会直接redirect到一个500错误页面。
目前来说,我们采用的记录日志的方式是写到本地文件,记录的格式是json。既然是json,那么文件内容会随时间的增大而变得巨大,所以我们会定时的清理这些文件。而且清理这些文件还有另外一个很重要的意义,等会会在日志的发送中提到。

####2、 日志的发送

既然是不断的追加文件,那如何把这些新加的内容实时传输到处理机上呢?首先,我们考虑了以下几种方式:

  • 1)周期性 scp,就是每隔一段时间往处理机上发。这个想法很好,而且也被很多公司用到,主要是消耗资源比较小。但是这会有一个很必要的条件,就是建立处理机和线上10多台机器的信任关系。当然,建立信任关系不是不好,其实是op不给加。而且确实不太安全,一旦一台机器被攻破,所有机器都被攻破了。

  • 2)既然不能scp,那我周期性的传一堆内容如何。这个一度被认为是最合适的方法,因为这样能解决信任关系,也能达到目的,但后面我们仔细分析了下实际情况,我们表示,我们还能这样:

  • 3)实时的往处理机上推错误日志。经过分析,一般情况下每台线上机器产生的错误日志并不会太多,他不像数据挖掘分析的日志那样,每天每台机器会产生好几百G的日志,那个量级已到了分布式存储的量级,我们不予考虑。所以,对于这样量级的错误日志,我们一台机器,实时的处理就绰绰有余了。不过从目前的情况来看,不是太乐观,还是有node处理不过来的情况,有时候也会因为内存溢出而down掉。

那如何发呢?我们目前采用的是node的tcp服务,通过tail -f管道实时的读文件的内容。

小结:日志的发送这一环节还是要case by case。日志量大,建议周期性scp;日志量小,建议实时的推送。要求降低磁盘io,建议直接放内存。要求降低内存,建议tail -f。

##二、日志的处理

接收——分类——分析——入库——报警

####1、详细分析,分类

一般会有一个比较大的分类,就是通过错误等级info、error、api、fatal四种错误归好类。然后再通过一些特定的信息继续分类,例如from字段,这种情况会在后面说。为什么要分类呢?因为每种不同的类型将有不同的报警、入库机制。
我们会把拿到的数据进行再次分析,例如分析出每种错误消息发生的频率、危机程度、可信度(可信度是指是不是恶意请求,当然这种恶意请求应该在服务器上就过滤掉)等等。

####2、报警机制

  • 1) 不同情况,不同报警
  • 2) 尽量简洁明了
  • 3)及时找到问题是谁造成的

当我们发现某一时刻出现的api错略多时,我们会第一时间自动发邮件和短信。当然,根据我们的分析,不同类型的错误消息会有不同的报警机制,包括报警的周期、阈值、时间段都是可以配置的错误。当然发出的邮件、短信要尽量简洁明了,这样做是为了清楚的反应问题和减少查错成本。
如果有必要,可以实时的找到问题是谁造成的最好。目前我们采用的做法是读svn的log。

####3、入库(难点)

既然是日志,我们就应该保存起来,目前是直接往mongo里面存档。(段子:作为一个初用mongo的小白,能实现基本的crud操作就好像找到了新大陆)。前面我们天真的以为这样的方式就能完美的解决问题,可是经过了一周之后,就发现入库的速度越来越慢,查询的
速度也越来越慢。我们还是so young do naive。那后来我们找了一系列的解决方案,包括

  • 1)尽量减少入库的内容
  • 2)增加索引
  • 3)做到分表分库,不要掉在一棵树上
  • 4)查询的时候必要的时候用下缓存,虽然并没有太多人去用

##三、日志可视化:

    1. 错误日志的内容管理系统,包括一些基本的crud操作,趋势,分配任务等等。
    1. 日志的报告,报告用来反应一段时间日志情况,目前报告比较简单,只反映一些类型的错误的数量和基本情况。
    1. 日志的详情(核心)。这里才是fe和rd们所要真正做的东西,在详情页,你可以看到某个错误的具体的内容,包括时间、出错的接口,数据等等,这就是查询的依据。

##四、项目里用到的一些库

    1. node-schedule 处理一些定时的任务
    1. async 连续嵌套5个callback是一种怎样的体验?
    1. node-mailer 发邮件

##五、what shall we do?

1、提交代码尽可能详细写好注释,方便排查问题
2、尽量经过code review
3、做好容错
4、尽可能少的请求后台接口,能缓存的尽量缓存。包括在node缓存,浏览器缓存等等。

##六、what we’ve got from eagle?

1、谁在刷接口一目了然,通过一些手段(user-agent、ip)能即时屏蔽那些熊孩子们;以前就碰到一个用http-agent的人,一直在刷。

2、双十一
全民疯抢的时候,大家不用一直盯着反馈,可以专心做自己的事情。真正出问题的时候,可以直接去定位问题。

3、让新来的同学练练手,让他们发现node的乐趣,至少我就是在这个项目中发现node的乐趣的。同样可以用在php,java,go,甚至c++等语言。

4、前面看到每天有不少的邮件过来,作为团队的一员,其实很失落。发现监控起作用的时候,真的很开心。。。原谅我如此这般…

最后给大家贴一下演讲时候的pdf的地址:
http://www.hirra.cn/links/eagle/eagle-w3ctech分享-韩路.pdf