方便地在后端登录。Yandex报告

有些事情总是不按计划进行。您必须回答以下问题:“怎么了?”,“为什么放慢速度?” 和“为什么我们以前没有看到这个?” 以一个简单的应用程序为例Daniil GalievZefiriorYandex.Travel的文章显示了如何回答这些问题以及哪些工具可以帮助您。我们将在一个方便的界面中设置日志记录,附加跟踪,扩展错误以及所有这些。



- 让我们开始吧。我将向您介绍便捷的日志记录以及围绕日志记录的基础结构,您可以部署这些日志记录和基础结构以方便您使用应用程序及其生命周期。







我们做什么?我们将构建一个小的应用程序,我们的启动。然后,我们将介绍基本的日志记录,这是该报告的一小部分,Python提供了现成的功能。然后是最大的一部分-我们将分析调试,推出和解决问题的工具时遇到的典型问题。



小小的免责声明:我将使用诸如pen和locale之类的词。让我解释。“句柄”可能是yandex s语,它表示APU之前的API,http或gRPC API或任何其他字母组合。“语言环境”是指我在笔记本电脑上进行开发的时间。看来我已经讲完了我无法控制的所有单词。



书店申请



开始吧。我们的创业公司是“书店”。该应用程序的主要功能将是图书销售,这就是我们要做的。然后一点点填充。该应用程序将用Flask编写。所有代码段,所有工具都是通用的,并且是从Python中抽象出来的,因此可以将它们集成到大多数应用程序中。但是在我们的谈话中将是Flask。



角色:我,开发人员,经理和我挚爱的同事Erast。任何比赛都是偶然的。







让我们谈一下结构。它是一个微服务架构应用程序。第一项服务是“书籍”,即包含书籍元数据的书籍存储。它使用PostgreSQL数据库。第二个微服务是存储有关用户订单的元数据的交付微服务。机柜是机柜的后端。我们没有前端,因此在我们的报告中不需要。内阁汇总来自图书服务和交付服务的请求,数据。







我将快速向您展示这些服务的句柄的代码,即API Books。该句柄从数据库中获取数据,对其进行序列化,将其转换为JSON并将其返回。







让我们走得更远。送货服务。手柄完全相同。我们从数据库中获取数据,对其进行序列化并发送。







最后一个旋钮是机柜旋钮。它的代码略有不同。内阁句柄从送货服务和预订服务请求数据,汇总响应并向用户下达订单。一切。我们很快找到了应用程序的结构。



应用程序中的基本日志记录



现在让我们讨论一下基本日志记录。让我们从术语开始。







Python给了我们什么?四个基本的主要实体:



-记录器,用于登录代码的入口点。您将使用某种Logger,编写logging.INFO,仅此而已。您的代码将不再对消息的去向以及接下来发生的事情一无所知。 Handler实体已经对此负责。



-处理程序处理您的消息,并决定将消息发送到的位置:是标准输出,文件还是其他人的邮件。



-过滤器是两个辅助实体之一。从日志中删除消息。另一个常见的用例是数据填充。例如,在您的帖子中,您需要添加一个属性。过滤器也可以为此提供帮助。



-格式化程序将您的消息转换为所需的形式。







这是我们完成术语的地方,我们将不再返回直接使用基类在Python中进行日志记录。但是,这是我们的应用程序配置的一个示例,该示例在所有三个服务上都已推出。对我们来说,有两个主要的重要块:格式化程序和处理程序。对于格式化程序,这里有一个示例,您可以在其中看到有关如何显示消息的模板。



在处理程序中,您可以看到logging.StreamHandler被使用。也就是说,我们将所有日志转储到标准输出。就是这样,我们已经完成了。



问题1.日志分散



继续解决问题。首先,第一个问题是:日志分散。



有点背景。我们已经编写了应用程序,糖果已经准备好了。我们可以靠它赚钱。我们正在将其投入生产。当然,有不止一台服务器。根据我们的保守估计,我们最复杂的应用程序大约需要三到四辆车,以此类推。



现在的问题。经理跑来找我们,问:“那儿坏了,救命!”你在跑步。一切都为您记录下来,太好了。您去第一台打字机看,您的请求没有。转到第二辆车-什么都没有。等等。这很不好,必须以某种方式解决。







让我们形式化我们想要看到的结果。我希望原木放在一个地方。这是一个简单的要求。更酷的一点是我想搜索日志。就是说,是的,它位于一个地方,我可以撕裂,但是如果除了简单的抓握之外,还拥有一些工具和出色的功能,那就太酷了。



而且我不想写。这个Erast喜欢写代码。我不是在说这个,我马上就做了产品。也就是说,您只需要较少的额外代码,只需一个或两个文件,行即可。







可以使用的解决方案是Elasticsearch。让我们尝试提高它。 Elasticsearch有什么好处?这是一个日志搜索界面。开箱即用,这不是您需要的控制台,而是唯一的存储位置。也就是说,我们满足了主要要求。我们不需要去服务器。



在我们的例子中,这将是一个相当简单的集成,并且在最新版本中,Elasticsearch具有一个负责大多数集成的新代理。他们自己在那里看到了整合。很酷。我之前写过一篇演讲,并使用filebeat一样简单。对于日志来说很简单。



关于Elasticsearch的一些知识。我不想做广告,但是还有许多其他功能。例如,很酷的事情是开箱即用的全文日志搜索。听起来很酷。目前,这些优势对我们而言已经足够。我们将其固定。







首先,我们将需要部署一个代理,该代理会将日志发送到Elasticsearch。您在Elasticsearch注册一个帐户,然后将其添加到您的docker-compose中。如果没有docker-compose,则可以使用句柄或在系统上进行提升。在我们的示例中,添加了以下代码块,并将其集成到docker-compose中。一切,服务已配置。您可以在volumes块中看到filebeat.yml配置文件。







这是一个文件beat.yml示例。在这里,我们建立了一个自动搜索在附近旋转的Docker容器的日志的功能。这些日志的选择已自定义。根据条件,您可以在容器上设置,悬挂标签,并根据此条件,仅从某些容器发送日志。处理器:add_docker_metadata块很简单。我们在Docker上下文中将有关您的日志的更多信息添加到日志中。可选,但很酷。







我们有什么?那就是我们编写的所有代码,非常酷。同时,我们将所有日志放在一个地方,并且有一个界面。我们可以搜索日志,这是搜索栏。他们已交付。您甚至可以实时打开它,这样流就可以在界面中跳转到我们的日志,并且我们看到了它。







我自己在这里会问:为什么,如何扣东西?什么是日志搜索,在那里可以做什么?



是的,使用这种方法开箱即用,当我们有文本日志时,会有一个小插科打::我们可以通过文本设置请求,例如消息:用户。这将向我们显示所有包含用户子字符串的日志。您可以使用星号,以及大多数其他的UNIX通配符。但这似乎还不够,我想加倍努力,以便您尽早在Nginx中进行预热。







让我们从Elasticsearch退一步,尝试不使用Elasticsearch,而是尝试另一种方法。让我们考虑一下结构日志。这是您的每个日志条目不仅是文本行,而且是序列化的对象,该对象具有任何第三方系统都可以序列化以获得现成对象的属性。



这有什么好处?它是统一的数据格式。是的,对象可以具有不同的属性,但是任何外部系统都可以读取JSON并获取某种对象。



某种打字。这简化了与其他系统的集成:无需编写解串器。而反序列化器则是另一点。您无需在应用程序中编写平淡无奇的文字。示例:“用户带有这样的ID专家,这样的订单。”所有这些都需要每次编写。



这困扰了我。我要写:“请求已到达。”进一步:“某某某某某某某某”,非常简单,非常具有IT风格。







让我们继续前进。让我们同意:我们将以JSON格式登录,这是一种简单的格式。立刻支持Elasticsearch,filebeat,我们对其进行序列化并尝试归档。这不是很困难。首先,您将设置文件从pythonjsonlogger库添加到JSONFormatter formatters块中,在此存储配置。这可能在系统中的不同位置。然后在format属性中,传递要添加到对象中的属性。



下面的块是添加到filebeat.yml的配置块。在这里,开箱即用的是一个用于解析JSON日志的filebeat接口。很酷。就是这样您不必为此写其他任何东西。现在,您的日志看起来像对象。







我们在Elasticsearch中得到了什么?在界面中,您可以立即看到您的日志已成为具有单独属性的对象,通过它您可以搜索,创建过滤器并进行复杂的查询。







让我们总结一下。现在,我们的日志有了结构。它们易于使用,可用于编写智能查询。由于Elasticsearch解析了所有这些属性,因此它知道这种结构。在kibana(Elasticsearch的接口)中,您可以使用Elasticsearch提供的专用查询语言过滤此类日志。



而且比划桨容易。Grep的语言相当难懂。有很多要写的。在kibana中可以简化许多事情。与此配合。



问题2.刹车



下一个问题是刹车。在微服务架构中,到处都是刹车。







这是一个小背景,我会告诉你一个故事。经理是我们项目的主要角色,跑来对我说:“嘿,办公室在变慢!丹妮,保存,帮忙!”



我们还什么都不知道,我们进入Elasticsearch的日志。但是,让我告诉您实际发生了什么。







Erast添加了一项功能。在书籍中,我们现在不显示作者的ID,而是在界面中直接显示他的名字。很酷。他使用以下代码完成了此操作。一小段代码,没有什么复杂的。可能出什么问题了?



训练有素的眼睛可以说,您不能使用SQLAlchemy和其他ORM来做到这一点。您需要进行预缓存或其他操作,以免循环中只有一个小的子查询进入数据库。一个不愉快的问题。看来根本不可能犯这样的错误。



让我告诉你。我有经验:我们与Django合作,并且在项目中实现了自定义预缓存。多年来一切进展顺利。在某个时候,Erast和我决定:让我们紧跟时代,更新Django。自然,Django对我们的自定义缓存一无所知,并且界面已更改。普里卡什无声地掉下来。这没有在测试中发现。同样的问题,更难抓住。



有什么问题?我该如何帮助您解决问题?







告诉您在开始解决刹车问题之前我做了什么。



我要做的第一件事是去Elasticsearch,我们已经有了它,这很有帮助,不需要在服务器上运行。我转到日志,寻找内阁日志。我发现长查询。我在一台笔记本电脑上播放它,发现阻碍了它的不是办公室。减慢书籍速度。



我遇到了“书籍”日志,找到有问题的查询-实际上,我们已经有了它。我在笔记本电脑上以相同的方式复制书籍。非常复杂的代码-我什么都不懂。我开始调试。时间很难把握。为什么?在SQLAlchemy内部很难确定这一点。我编写自定义时间记录器,本地化并解决问题。







伤了我困难,不愉快。我哭了。我希望这个发现问题的过程更快,更方便。



让我们形式化我们的问题。在日志中搜索速度下降是很困难的,因为我们的日志是不相关事件的日志。我们必须编写自定义计时器,以向我们显示已执行了多少代码块。此外,还不清楚如何记录外部系统的时间:例如,ORM或请求库。我们需要将计时器嵌入到内部或与某种包装器一起使用,但是我们不知道为什么它会在内部变慢。复杂。







我发现Jaeger是一个很好的解决方案。这是opentracing协议的实现,因此让我们实现跟踪。



积家给什么?它是带有搜索查询的用户友好界面。您可以过滤较长的查询,也可以按标签进行过滤。请求流的直观表示,非常漂亮的图片,我稍后再展示。



定时记录开箱即用。您不必对他们做任何事情。如果需要检查正在运行多少自定义块,可以将其包装在Jaeger提供的计时器中。非常舒适。







让我们看看如何在界面中找到问题并将其本地化。我们进入Jaeger界面。这就是我们的要求。我们可以搜索帐户或其他服务的请求。我们立即过滤长查询。我们对长记录感兴趣,很难从日志中找到它们。我们得到他们的名单。







我们进入此查询,并且看到了很多SQL子查询。我们可以清楚地看到它们是如何及时执行的,由哪个代码块负责。很酷。而且,就我们的问题而言,这不是完整的日志。还有另外两块或三块滑下来的大鞋垫。我们很快在Jaeger中定位了该问题。解决问题之后,Jaeger提供的上下文可以为我们提供什么帮助?







Jaeger日志,例如SQL查询:您可以查看重复的查询。非常快又酷。







我们解决了问题,立即在Jaeger中看到一切都很好。我们通过相同的查询检查我们现在没有子查询。为什么?假设我们检查相同的请求,找出时间-在Elasticsearch中查看请求执行了多长时间。然后,我们将看到时间。但这不能保证没有子查询。在这里,我们看到了,很酷。







让我们实现Jaeger。不需要很多代码。您为Flask添加了opentracing的依赖项。现在了解我们正在执行的代码。



第一个代码块是Jaeger客户端设置。



然后,我们与Flask,Django或任何其他具有集成功能的框架建立集成。



install_all_patches是最后一行代码,也是最有趣的代码。我们通过与MySQL,Postgres,请求库进行交互来修补大多数外部集成。我们正在修补所有这些,这就是为什么在Jaeger界面中我们立即看到所有使用SQL进行的查询以及我们所需的服务去了哪些服务。很酷。而且您不必写太多。我们只是写了install_all_patches。魔法!



我们有什么?现在,您无需从日志中收集事件。如我所说,日志是完全不同的事件。在Jaeger中,这是您可以看到的重大事件。Jaeger允许您捕获应用程序中的瓶颈。您只需搜索较长的查询,就可以分析出了什么问题。



问题3.错误



最后一个问题是错误。是的,我很狡猾。我不会帮助您摆脱应用程序中的错误,但是我会告诉您下一步您可以做什么。







上下文。您可以说:“ Danya,我们正在记录错误,我们有500个警报,我们已经配置了它们。你想要什么?我们记录了日志,然后进行了日志记录和调试。



您从日志中不知道错误的重要性。重要性是什么?在这里,您会遇到一个很酷的错误,即连接数据库的错误。基地刚刚失败。我想立即看到这个错误不是那么重要,如果没有时间,请忽略它,而要修复更重要的错误。



错误率是可以帮助我们对其进行调试的上下文。如何追踪错误?让我们继续,一个月前我们犯了一个错误,现在又出现了。我想立即找到解决方案并进行更正,或者将其外观与其中一个版本进行比较。







这是一个很好的例子。当我看到与Jaeger的集成时,我做了一些更改。我已经更改了应用程序响应的格式。我得到这个错误。但是尚不清楚为什么我没有钥匙,订单对象中有很多东西,没有什么对我有帮助。就像,在这里看到错误,重现并自己捕获。







让我们实现哨兵。这是一个错误跟踪器,将帮助我们解决类似的问题并查找错误的上下文。采取由哨兵开发人员维护的标准库。在四行代码中,我们将其添加到我们的应用程序中。一切。







我们在出行途中得到了什么?这是带有错误的仪表板,可以将错误按项目分组,您可以按照以下说明进行操作。大量错误日志的组成部分被分为相同,相似的错误日志。为他们提供统计信息。您还可以使用该界面处理这些错误。







让我们看看我们的例子。陷入KeyError。我们立即看到错误的上下文,订单对象中的内容,不存在的内容。我立即误认为Delivery应用程序为我提供了一个新的数据结构。内阁根本没有为此做好准备。







哨兵除了我列出的东西以外还能提供什么?让我们正式化。



这是您可以在其中查找它们的错误存储。有一些方便的工具。错误归类-按项目,按相似性。 Sentry提供与不同跟踪器的集成。也就是说,您可以跟踪您的错误并与他们一起工作。您只需将任务添加到上下文中就可以了。这有助于开发。



错误统计信息。同样,与推出发行版相比,这很容易。哨兵将帮助您。错误旁边发生的类似事件也可以帮助您找到并了解导致错误的原因。







让我们总结一下。我们编写了一个简单但可以满足需求的应用程序。它可以帮助您在其生命周期中进行开发和维护。我们做了什么?我们已将日志收集到一个存储库中。这使我们有机会不在不同的地方寻找它们。另外,我们现在有了日志搜索和第三方功能,即我们的工具。



集成跟踪。现在,我们可以直观地监视应用程序中的数据流。



并添加了一个方便的工具来处理错误。无论我们如何努力,它们都会出现在我们的应用程序中。但是我们会更快更好地修复它们。



您还能添加什么?应用程序本身已经准备好,有一个链接,您可以看到它是如何完成的。所有集成都在此进行。例如,与Elasticsearch集成或跟踪。进来看看。



我没时间讲的另一个很酷的事情是requests_id。几乎与跟踪中使用的trace_id相同。但是我们负责request_id,这是它的最重要的功能。经理可以立即带requests_id来找我们,我们不需要找他。我们将立即开始解决我们的问题。很酷。



并且不要忘记我们已经实现的工具是开销。这些是每个应用程序都需要解决的问题。您不能无所顾忌地实现我们的所有集成,使您的生活更轻松,然后考虑如何使用禁止应用程序。



一探究竟。如果不影响您,请冷静。您只会得到加分,而不能解决制动器的问题。不要忘记这一点。谢谢大家的收听。



All Articles