前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >浅显地聊一聊中小公司的日志系统与Tracing(上)

浅显地聊一聊中小公司的日志系统与Tracing(上)

作者头像
老李秀
发布于 2022-04-29 12:53:28
发布于 2022-04-29 12:53:28
8350
举报

嗨,大家好,我是说发文章就发文章的谢顶道人 --- 老李,我最近赢得了一个称号:下周发文谢顶老道,和下周回国那位快打平了。

我最近被催的要死要活的,所以我必须要写一篇接地气的关于工程应用领域的文章,我想来想去:就写一下我最近正好简单调研过的日志吧,而且是中小公司的日志,我结合自己过往的经历来很主观地唠唠日志系统。

老李环顾了一下四周,看看大伙儿都在说什么,比如这位:

又比如这些后台里发消息的宝贝儿们:

当然了,还有一些给我发电视剧剧照的:

日志,一坨彼此之间物理上毫无关联的离散状态的字符串。首先我们需要统一思想:当业务系统遇到任何问题时候只能依靠日志中的蛛丝马迹去追查原因,所以日志必须要记录得好且全、记录得有格式化且有意义,而且有些错误即便在日志做到了前面两个得的前提下,也依然不好查询到原因,宛如隐疾定时炸弹。

本文简要目录:

一、统一思想

二、小公司的合理做法

三、中小公司在带有一丝丝服务拆分时的合理做法

四、延伸:MOMO和打车公司的解决思路

让我们先统一好思想

我在此将日志分为两类:

一、系统级系统:大家做的基本上都是服务器编程,所以我认为每条网络请求的日志都应该一条不落的记录下来,注意是一条不落,具体实现方式就是在请求收口处/拦截器/中间件处去统一收敛处理;除此之外,系统级日志还包括框架本身在遇到代码错误或异常时,框架记录的日志(比如Go可能出现的Panic、Java中可能出现的OOM、连接数据库失败等等)。

二、业务级日志:可能有人觉得上面每条网络请求的日志应该属于业务日志,我觉得无可厚非,以你为准。我所认为的业务日志是泛指业务研发在业务代码中为了便于调试或者发现问题而留下的Log::info()。

我在此说下我对所谓好的日志的理解与定义:

一、「系统与业务日志」统一格式化:JSON或者约定间隔符号比如||、或者双||,且日志尽量要以为key:value键值对方式,JSON是最粗暴的懒人做法

二、「系统日志」必含字段:注意此条只针对系统日志,至少应该包含请求接口名称(API)、请求方IP(Caller)、请求方入参数(Args)、服务方的返回结果(Resp)与本次调用耗费的时间(Took-Sec)。如果是HTTP服务,甚至HTTP Header也是有必要留下的

三、「系统与业务日志」必含字段之Request-ID:无论是系统日志还是业务日志,都应该记录该请求的Request-ID,Request-ID一定要由客户端去产生,接入层可以做兜底补充,这样客户端的请求可以与该请求在后续产生的所有日志很容易很自然的串联起来,这个问题我觉得不要问为什么或者是否有必要(文章后面我们会讨论在涉及到一定量微服务时候关于Tracing与日志讨论)

五、「系统与业务日志」必含字段之UID或者DID(设备ID):在可以拿到UID的情况下一定要拿到UID并作为必需字段注入到日志中。为什么?因为很多时候锁定用户业务问题之前,你需要率先通过时间范围 + UID去搜索出用户在该时间段内所产生所有的接口访问记录;而在某些情况下无法得到用户UID的时候,DID则是必要补充手段(比如登陆时,用户可能就是没有UID的,此时DID则成了救命稻草)

六、日志目录统一化:这个仅仅是推荐,但并不强制。目录统一化的好处是对于日志收集工具、日志grep工具等会友好一些,我认为此项属于option选项,并不用强制化

七、日志组件统一化:其实一到六的所有问题,通过一个统一的日志组件就可以一劳永逸地全部解决掉。日志组件不需要你自己发明,直接找开源的包裹一下,适配一下自己的业务系统即可

我在此将故障分为两类:

一、系统级故障:我们认为此时由于各种各样的致命级错误,导致服务代码无法完成,比如PHP的Fatal级错误、Go中Panic等等

二、业务级故障:这应该是我们最常遇到的错误,也是最棘手的错误,本质上也不算错误,其实就是系统运行结果与用户操作预期不符合。比如用户下单,优惠券用上了说好要付10块,结果付了12块;或者用户预期看到的是结果A,结果返回的结果是B。此时系统并没有崩溃,只是业务代码中纰漏导致了最终结果

单体架构的小公司

明智的小公司业务系统跟业务量相对应,一般都是单体架构,后端研发人员数量大概在10-30人左右(上下误差10左右),此阶段最好的选择是云服务商的云日志服务,次级选择是自建ELK,最次是登陆到服务器上去查看日志(这种情况下如果有两台以及以上服务器,恐怕是很痛苦的一件事,有些同学此时会甚至编写网络版的grep工具在跳板机上去grep所有机器上的日志)。

所以针对这种情况,解决方案很简单:

一、第一步:如果认同前面「日志分类」与「良好日志格式」的同学,先实现这里或者实现以你为准的日志,总之目的只有一个---良好且有意义的日志

二、第二步:选择日志数据收集系统与检索系统,我认为按照顺序是云服务日志平台 > 自建ELK > 自己登陆到服务器上搞

在建设完成的情况下,一般遇到问题需要诊断时候:

一、如果是针对用户无法Request-ID时候,需要结合时间范围+UID锁定用户去搜索出用户在该时间段内产生的API请求记录,然后结合遇到问题的业务场景(也就是网页界面或者APP界面)锁定N条API请求,根据Request-ID查看本次请求中产生的所有日志,继而进一步锁定故障原因

二、如果是可以确定的某一条Request-ID请求,往往出现在测试或者联调阶段,这种的直接利用Request-ID搜索即可

带有一丝丝服务化性质的中小公司

具备一定的业务流量的中型公司,同时由于业务上已经具备了一定复杂程度做了一定的服务化措施,已经将一些较为公共的代码已经单独实现为独立的小服务,技术团队人数在百人规模左右。

一、第一步:如果认同前面「日志分类」与「良好日志格式」的同学,先实现这里或者实现以你为准的日志,总之目的只有一个---良好且有意义的日志

二、第二步:一定要选择云服务商提供的日志服务

查询与锁定问题的方式基本上与前面描述一致,相信聪明的你们也各有各的方式。但是,截止到目前这个规模,对日志链路记录与链路还原,已经产生一定量但并不一定是必需的诉求,什么意思呢?

老李举个例子,先来看一下中小规模公司可能出现的业务系统组织方式:

在这种情况下,A、B、C与原巨无霸系统任意一个服务节点如果由于业务代码逻辑问题出现的业务故障(非系统故障,参考文前统一思想处的故障分类),都一定会导致最用户获得的实际结果与预期不符。

但是还好,不怕出问题,就怕出了问题不好查,查了赶紧修复止损就是好同志。截止到目前为止,如果你认同我所说的日志中必要字段之Request-ID,我觉得在这个业务故障是很容易被发现并锁定的。因为Request-ID可以串联出本次请求中所到达的服务节点中所有服务所产生的每一条请求日志,总结一下:

一、原巨无霸系统、A、B、C的系统日志中均记录每一条请求

二、每一条系统日志均包含至少入参(Args)与返回结果(Resp)

三、这些分散隔离的日志被必含字段Request-ID可以串联起来

因为你的业务日志以及系统日志满足上面三点,所以你可以很快锁定到底是哪一部分出了问题。你只需要在日志云服务或自建ELK上根据UID + 时间锁定API调用记录范围 ---> 锁定某条确定的API调用记录 ---> 根据Request-ID锁定所有日志,三步锁定故障范围。

截止到目前为止,上面的这套标准与守则,在一个标准Log组件与日志存储检索服务的合理搭配下,是完全可以极其快速并高效完成绝大多数业务故障。

并且如果你们严格遵守上面我们说好的约定,甚至还会有意外收获。不知道你还是否记得我们在日志中记录了一个Took-Sec,此时我们可以每天定时对这些日志进行离线抽样分析汇总,我们可以得到服务对外接口的平均耗时与接口调用占比,最终甚至可以得到访问量排名前N的接口的平均耗时为多少。

因为我们的日志中,还包含了系统故障日志,所以甚至可以在日志中出现故障时,及时发出报警。或者在某一次上线后,发现某个接口的平均耗时比昨天增加了XXX,这种情况也可以配置触发报警。

是的,合理的利用好「好的」日志可以几乎搞定一切,本质上说即便是Trace信息抑或Metric信息,说到底也是由文本记录的信息进行时序汇总分析后的结果,说不好听了:就是特立独行化的日志。

好了,有点儿跑偏了,让我们回到正途。

总结一下

上面的日志系统看起来不错吧?一个Request-ID帮了我们很多忙,现在我想给这个Request-ID去启用另外一个名字:TraceId。所以让我们再来一遍:一个TraceId帮了我们很多忙。但是这就满足了吗?并没有,我来说下缺陷是什么。

首先,由于业务系统相对不那么复杂,这个TraceID足以应付,但是实际上无论你用ELK或者日志云服务,都会存在两个至少会出现其一的问题:

一、如果A、B、C与原巨无霸服务的日志都存储在同一个空间中,根据TraceID搜索出的日志会出现一个网页中,由于服务不多,你还是可以用肉眼人脑人肉的方式去组织一下日志的调用顺序与调用层次关系,但是总归是需要你用人肉方式去还原

二、如果A、B、C与原巨无霸服务的日志是分开存储的,分别存储在自己的存储空间内,恭喜你,你需要拿这个TraceID分别打开个Tab页去搜索四个系统的日志,然后再人肉串联。同时记得,不要忘了找运维给你开通云服务的权限

那么有更好的方式去还原请求发生时日志的时序顺序与调用层次顺序吗?比如我用图来简单勾勒一下上面图中业务系统一次请求所产生的日志应该是怎样展现会更好:

不好意思,灵魂画手了一把。图想表达的意思就是:通过在一个搜索框中输入TraceID进行搜索,与本次TraceID会话产生的所有日志均将被搜索到,并且按照时间顺序与调用层次关系,展现在同一个网页中,你只需要拖动鼠标依次展开每条日志,很方便的就能看到每个服务日志的入参与返回,甚至可以反馈出耗时图形。

有办法可以按照上面的方式去还原一个请求产生的日志历史现场吗?有。如果说可以直接利用的话,那就是可以借鉴一下Tracing机制,这个东西又有另外一个名字:分布式链路跟踪系统。

无论是老李之前在MM还是在互联网打车公司,那个日志系统做的堪称一绝,一些同学在离职后直呼当初没有研究一下那玩意,面对新入职的公司日志只能陷入无限怀念中:

但是Tracing和日志,有什么关系与不同呢?老李之前所在的公司中,好的解决思路是什么呢?中小公司能否利用「开源组件」+「云服务」去用较小的代价去无限接近这种效果呢?

拜拜,下周。。。哦不,下篇文章见。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能API社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
领域驱动系列三
领域模型是软件项目中的核心,模型是团队经过长时间的归纳总结形成的一个与项目有关的概念集合,他用术语和关系表达了领域的深层含义,这种关系和语义提供了模型语言的语义,模型语言是为领域独家定制的.十分的精确,并且他将开发过程和模型绑定到一起,并使代码和模型紧密的绑定.
郑小超.
2019/01/03
4070
DDD理论学习系列(1)-- 通用语言
1.引言 在开始之前,我想我们有必要先了解以下DDD的主要参与者。因为毕竟语言是人说的吗,就像我们面向对象编程一样,那通用语言面向的是? DDD的主要参与者:领域专家+开发人员 领域专家:精通业务的任何人。 开发人员:开发+测试。 领域专家擅长某个领域的知识,专注于交付的业务价值。 开发人员则注重于技术实现。 开发人员总是想着类、接口、方法、设计模式、架构等。以面向对象的编程思想进行思考,思考如何进行抽象、封装、继承、多态等。而领域专家对软件中的框架、持久化、数据库等没有概念,而这也就导致了他们
圣杰
2018/01/11
1.5K0
领域驱动设计学习之路—DDD的原则与实践
本文是我学习Scott Millett & Nick Tune编著的《领域驱动设计模式、原理与实践》一书的学习笔记,一共会分为4个部分如下,此文为第1部分:
Edison Zhou
2019/05/10
2.1K0
领域驱动设计学习之路—DDD的原则与实践
字节一面挂了,面试官问DDD,我却不知道
在介绍DDD(Domain-Driven Design:领域驱动设计)之前,我们先回顾一下,现阶段的产品迭代中常常出现的一些问题,以及这些问题会导致什么。通过对问题的总结和分析,再回头看一看,DDD能帮我们解决什么?
爪哇缪斯
2023/05/10
4840
字节一面挂了,面试官问DDD,我却不知道
谈一谈 DDD
最近 10 年的互联网发展,从电子商务到移动互联,再到“互联网+”与传统行业的互联网转型,是一个非常痛苦的转型过程。在这个过程中,一方面会给我们带来诸多的挑战,另一方面又会给我们带来无尽的机会,它会带来更多的新兴市场、新兴产业与全新业务,给我们带来全新的发展机遇。然而,在面对全新业务、全新增长点的时候,我们能不能把握住这样的机遇呢?
冬夜先生
2021/12/06
5110
[译文]Domain Driven Design Reference(二)—— 让模型起作用
  很多项目做建模工作最终没有获得太多的实际好处。DDD模式从项目中提炼成功的实践使得建模带来了巨大的好处。总的来说,他们提出了一个与先从细节再到高层次的视角【1,在DDD之前我们大部分都习惯于先进行数据表的设计】完全不同的建模和软件开发的方式。严格的建模惯例必须平衡好与非技术人员【2,一般这里指领域专家】合作进行的模型探索。战术和战略必须结合才能成功,DDD同时涉及战术和战略的设计。
Zachary_ZF
2018/09/10
3480
软件方法(下)第8章分析之分析类图—知识篇Part05(202205更新)领域专家和通用语言
一个领域之所以能作为“领域”为人认知,必定会在发展过程中沉淀出一套日益完善和精确的术语体系。每个术语有其独特的、其他术语不能替代的含义。
用户6288414
2022/05/27
4020
软件方法(下)第8章分析之分析类图—知识篇Part05(202205更新)领域专家和通用语言
领域驱动设计
关于领域驱动设计 这篇文章参考了Eric Evans《领域驱动设计》一书以及Jimmy Nilsson《以C# .NET为例运用领域驱动设计和模式》,二者详细描述了领域驱动设计的核心概念、技术和模式。在某些情况下,直接使用这些书的措辞是有意义的,并且我认为Eric Evans和Jimmy Nilsson也允许我们这么做。 尽管将方法本身呈现出来是很有用的,但是仅仅对方法进行描述,DDD的许多微妙之处就会消失。这些方法应该是你的工具,而不是你束缚你的规则。它们是为设计而生的语言,在团队内沟通创意和模型十分有益
程序猿DD
2018/03/21
1K0
领域驱动设计
DDD开篇总结
对于DDD的启蒙,不管是国内还是国外思维逻辑都是一样的。或者说如果你想写本关于DDD的书,大纲似乎是一样的
码农戏码
2021/03/23
5230
限界上下文是什么鬼?DDD 最抽象的概念详解
- 什么是通用语言 - 通用语言, 最主要的目的就是减少交流中信息丢失, 在实际开发中, 可能关联很多人, 例如有业务层面的业务细节制定者、领域专家、产品经理、项目经理 、架构师、开发
玄姐谈AGI
2021/07/29
6.3K0
DDD领域驱动实战 - 限界上下文(bounded context)
限界上下文定义领域边界,以确保每个上下文含义在它特定的边界内都具有唯一的含义,领域模型则存在于这个边界之内。
JavaEdge
2020/10/02
4.3K0
DDD领域驱动实战 - 限界上下文(bounded context)
[译文]Domain Driven Design Reference(五)—— 为战略设计的上下文映射
  两个组之间的关系是“上游”小组的行为影响“下游”小组的项目成功。但下游的行为并不会显著影响上游项目。(例如,如果两个城市沿着同一条河流,上游城市的污染主要影响下游城市。)
Zachary_ZF
2018/09/10
3580
真下饭!字节技术官DDD(领域驱动设计)手册,拆解业务代码首选
至少20年前,一些顶尖的软件设计人员就已经认识到领域建模和设计的重要性,但令人惊讶的是,这么长时间以来几乎没有人写出点儿什么,告诉大家应该做哪些工作或如何去做。尽管这些工作还没有被清楚地表述出来,但一种新的思潮已经形成,它像一股暗流一样在对象社区中涌动,我把这种思潮称为领域驱动设计(domain-driven design)。
愿天堂没有BUG
2022/10/28
5510
真下饭!字节技术官DDD(领域驱动设计)手册,拆解业务代码首选
领域驱动设计(DDD)部分核心概念
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/03/30
4110
领域驱动模型(DDD)
2004年Eric Evans 发表《领域驱动设计——软件核心复杂性应对之道》(Domain-Driven Design –Tackling Complexity in the Heart of Software),简称Evans DDD,领域驱动设计思想进入软件开发者的视野。领域驱动设计分为两个阶段:
高广超
2018/12/12
3.9K0
走近DDD
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/03/04
4070
重读领域驱动设计——如何说好一门通用语言
在 DDD 中,通用语言是以限界上下文为边界的。如果一个产品或者项目有多个限界上下文,我们就需要为每个限界上下文定义通用语言。
ThoughtWorks
2019/05/05
6740
重读领域驱动设计——如何说好一门通用语言
混合开发:TDD、DDD和BDD交集的值
测试驱动开发(TDD)是一种开发软件的过程,其中在编写代码之前先编写测试。一旦完成,开发人员将努力编写足够的代码以通过测试,然后开始重构。
程序猿玄微子
2020/12/05
2K0
混合开发:TDD、DDD和BDD交集的值
DDD“通用语言”背后的倒退-《软件方法》节选
DDD(领域驱动设计)话语中有“通用语言(Ubiquitous Language)”的用语,这是一个伪创新。
用户6288414
2022/10/31
5020
DDD“通用语言”背后的倒退-《软件方法》节选
领域驱动设计模式的收益与挑战
《软件学报》在2021年第32卷第9期刊登了一篇论文:《领域驱动设计模式的收益与挑战:系统综述》[1]。这篇论文是学术界在这一领域开山之作。
码农戏码
2021/11/18
1.3K0
领域驱动设计模式的收益与挑战
推荐阅读
相关推荐
领域驱动系列三
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档