习惯每周写一篇基于技术的帖子,描述一些自己的想法和故事,一方面是整理自己的思路,另一方面是欢迎拍砖。
这几天看到一些论坛里的技术帖子,顺便梳理了一下2016所有的技术精英大赛的C++帖子。 把一些好的思想,结合自己的实践,变为自己的,很是愉快。 有时间,好好的和Billing的同事去学习一下区块链,那里真是一个宝库,呵呵。 说正文吧, 有几篇帖子说到日志的难管理,以及问题出现了,运维人员只能打电话给程序,让他们帮忙分析问题。 这是很多项目的通病。 程序一般喜欢打日志,这里打一点,那里打一点,弄的到处都是日志。 而实际运维系统上的人,基本看不懂这些日志,以至于日积月累,巨大量的日志,哪个可以删,哪个不可以删? 你让程序员自己去清理?靠谱么?可能这个开发说,"那天出问题了我还要用呢,别删!!" 又比如,周末,程序员正在休息,运维发现问题,打电话给程序,往往最多说的是。"XXX目前不正常", 负责一点的,"XXX进程好像挂了",程序听了这些,心里多半会说:"和没说一样,什么还得自己去看。" 这样的问题,我遇到过好多次了。 运维抱怨研发弄的乱七八糟,而研发抱怨运维只是一个高级传话筒。 这种隔阂,造成了很多项目的"内伤",这种"内伤"就像大堤的裂缝,那天说不定就造成大事件。 2008年,我开源了一套自己的服务器引擎,基于ACE的。 一开始用途就是想把自己从无尽的IO重写中拯救出来。当时做了好几个系统,由于接口关系,自己写了无数遍的select epoll。虽说都那么几百行代码反过来调过去,就是不爽。当时想法很简单,别人用不用我不管,我只要自己用的爽就行了。反正我是不想再每次都重复写IO了。 2011年,没想到民生银行的一个开发和我联系,问我关于这个开源系统的一些实现细节,他们要用。 当时觉得很有趣,就帮助他们借助我的工具实现了一些东西,权当提高自己的能力。 其中有一个细节特别值得我记忆和总结。 他们遇到的问题,就是外包的有些模块,日志到处都是,再加上各种交接,有些日志谁也不清楚到底是什么,有没有用。以至于不敢动,身为运维非常的头痛。 结合自己的经历,回想起自己的项目写的日志,时间一长,不看代码,都不知道在干什么的,自己有些脸红。 于是开始思考,如何让运维硬起来?(好的设计,必须从问题产出者的出发点去思考) 于是设计出了一套运维插件规则,直接理顺了当时的整合项目,起到了很好的效果。 今天先说日志部分。 在我看来,日志必须是运维的关键性检查指标之一。 甚至,在某些时候,它的强度和研发是一致的。运维懂研发的日志,那么更加就能直接的反应问题了。不再受必须研发出现的制约。 于是我就提出,身为运维,必须提供一个日志插件体系,所有的研发必须只能用此体系。 怎么理解这句话? 也就是,运维提供一个基于C++的日志系统插件,供研发使用。所有的研发不得使用自己的写日志文件。全部必须使用此日志接口的API实现自己的日志记录。(当时的项目是C++的,后来,这个插件扩展到支持Java和python) 再说的细一点,运维必须提供一个日志记录的功能代码集合,甚至是可以加载的so文件。具体根据项目而定。 分级日志插件系统就这么出来了。 首先,我写的这个日志插件,就是实现研发日常的日志记录。和程序员自己使用的记录日志代码本质上没有什么区别。 但是有一条,我的这个插件,支持规则限制。 什么叫规则限制? 也就是可以做到,运维可以任意配置日志规则,而这个配置规则,完全不需要研发参与。 怎么理解? 来看看,我的日志插件依赖的是一个XML的配置文件。这个配置文件,在测试阶段,是研发配置的,但是在生产环境,似乎运维配置的。
这里有几个参数稍微解释一下。 这是描述生成一个日志文件的具体描述。(文件规则目前是一天一个,以日期为后缀) logid是唯一的数字标识,用于匹配指定的日志文件模块,下面我会介绍如何使用它。 logtype目前支持两种状态,一种是正常流水日志(0),一种是异常日志(1)。这个由具体日志的使用者配置。 Display表示的是当前输出的日志,是否输出到屏幕,还是输出到文件。0是文件,1是屏幕 Level当前日志级别,这个和整个日志系统互动。一会我会详细说明。 logname日志文件名称,实际生成的日志名称的主词,我会根据这个名称自动生成一个子目录,并在里面根据每天的日志自动生成日志信息。 好了,有了这个,再说明一下运维需要设置的关键参数 CurrLevel当前日志插件的运行模式级别,这个怎么理解?每个日志模块都有一个Level字段。在进程运行的时候,凡是大于此级别的日志才会被输出,这样做,就等于放权给了运维,你决定,那些级别的日志在生产环境是输出成文件的,低于此级别的日志,全部不会生成文件。 所有的日志配置文件,全部由运维和研发商量给出定级标准。 而最终日志配置文件如何配置,由运维给出。 在开发运行过程中,严禁使用诸如printf,fprint之类的东西,或者自己的日志类。所有日志输出必须使用此插件的输出。 目前对研发,它支持3个API接口。研发不用知道日志是怎么实现的,只要先会用就行了。 //nLogType对应logid字段,研发只管写就行了,你不用管运维是如何配置你的日志文件级别。 int WriteLog(int nLogType, const char* fmt, ...); //二进制字段的输出 int WriteLogBinary(int nLogType, const char* pData, int nLen); //邮件输出 int WriteToMail(int nLogType, uint32 u4MailID, char* pTitle, const char* fmt, ...); 这里三个API基本能满足所有的日志要求了,文本,二进制,大家都知道。 那么第三个邮件输出,是指,你可以配置一个邮件服务器,当某些日志产生(研发决定),将会将当前日志输出成一封邮件发送出来给指定的邮箱。这个可以权当报警事件或者事件通知事件来处理,运维在生产环境下,可以通过xml来配置自己的邮箱实现对研发的某些约定进行邮件通知的机制。 运维在生产环境上配置的日志插件文件,决定了运维知道哪些日志是干什么的。那么,为什么要运行级别呢?有意思的来了。 我们在实际运行任务中,经常会发现某些数据不正常,需要打印出日志。 但是,如果我们在所有的地方都打印日志,就未免太多了,运维人员也会看花眼。 那么怎么办呢? 我们可以让运维在所有的日志等级上设置级别。 启动进程的时候,比如大于5级的日志,才会输出。 那么当出现问题的时候怎么办? 我们需要运维可以手动的"降级"当前生产环境的日志。这个阶段,进程不需要停止。发送一个指令,让日志插件从5级降到3级,输出所有大于3级的日志。 然后可以导出给研发,或者自己分析后给研发。 导出后,可以再将日志级别"升级"为正常的运维级别(不停机)。保留最少最必要的日志。 这样,就实现了,运维提供日志插件,而研发执行日志输出。至于实际生产环境如何配置,研发不用知道,只要协调好了即可。 这个思维后来被我扩展了,那就是,运维"插件化"的思想。 有时候,并不是所有的东西,都需要研发同事去做的,有些东西,完全可以从运维出发去做,做成可独立封装的类,交付研发,达到自己的目的,谁说运维不会编程?有些高级核心运维写程序比研发还要强呢。 插件化运维的核心思想,就是把自己要收集的资料收集到自己的手里。合理的分配权限,日志可不止只有你研发去看,运维也在看,而更多的是,运维可以动态的决定日志的输出,将极大的提高运维本身的效率。 最后的结果是,民生银行采用了这套运维架构,直接要求所有的第三方产生使用统一的日志模块,并接受日志管理。极大的减轻了运维"低级传话筒"的角色,通过日志的动态"升级" "降级"。甚至可以在不停机的时候导出必要的分析数据。直接升级到了更高级的管理者。并且,结合日志,运维可以开发各种工具实现"表格展示",比如一些通用的开源的图表展示系统,在这里就不介绍了,github自己搜搜吧。 运维人员决定,那些日志,我现在是需要的。而不是研发,毕竟,研发更专注的是实现,而不是运维过程。大家物尽其用,互不干扰。 好了,我的话说完了,希望大家多多提提意见。 哦,当然,我知道,或许你们也想知道代码是怎么实现的是吧,没问题。 附件就是日志插件的源码,有兴趣可以玩玩,你玩的越愉快,我越高兴。当然,欢迎交流