来自 安全 2022-06-08 20:10 的文章

高防IP_燕云盾娘_精准

Palantir已投入大量资金,以支持由我们的日志和遥测基础设施支持的远程调试工作流。有时,我们必须调查我们运行的第三方软件的问题,从而关注捕获这些组件的日志和度量。结合正则表达式和静态代码分析,我们能够从这些非结构化日志中提取结构化信息。这篇博文概述了我们是如何做到这一点的。

高防IP_燕云盾娘_精准

我们的日志和遥测基础设施

Palantir产品、Gotham和Foundry的后端和前端组件,以结构化JSON格式(而不是非结构化文本)发出日志和度量信息。这些结构化日志行如下所示:

{"type":"service.1",message":"Accessing dataset.","level":"DEBUG","params":{"dataset":"my dataset",},"time":"2019-07-28T09:31:04.46165-07:00"}

注意,这些结构化日志避免了通过字符串格式将参数插入日志消息的常见日志做法,创建可变消息。具有已知模式的结构化日志有助于索引、搜索和分析日志信息。例如,我们可以对日志数据库运行查询,以统计此特定日志条目的出现情况:

选择*FROM service.1-logs,其中message="Accessing dataset"。

结构化日志还允许更复杂的查询,服务器防御CC软件,如日志中特定字段的聚合。日志聚合提供了一种深入调试的方法。例如,如果我们通过params.dataset字段对查询结果进行调整,我们可能会学到一些东西:

从service.1-logsWHERE message="Accessing dataset."按params.dataset分组

(旁白:我们在查询这些日志时实际上不使用SQL,这个查询的结果可能会揭示某个数据集周围的异常行为,并提示我们下一步应该去哪里。日志查询的这种灵活性极大地提高了产品团队在操作上对其软件负责的能力,并使得即使是小团队也可以在部署的任何地方(通常有数百个安装)支持产品。不过,这里的覆盖范围有一点差距-支持和调试不以我们期望的格式生成日志的开源软件如何?

使第三方日志可消费

我们的Rubix团队(Rubix是我们Kubernetes基础设施的名称)运行第三方软件,如etcd、vault、,和Kubernetes,它们不生成这种格式的日志,ddos攻击高防御服务器,但我们仍然希望这些日志通过我们的管道,以便产品开发人员能够可靠地为Palantir编写的软件和第三方软件使用相同的调试工作流。一年多以前,我们写了一个叫做planer的小工具来解决这个问题。Planer使用正则表达式将Palantir的标准日志结构应用于第三方软件的非结构化日志行,从而允许我们通过同一日志管道对第三方进行索引和分析。

Planer的缺点

编写正则表达式很难-手工制作这些正则表达式可能非常耗时,而且生成的正则表达式通常是脆弱的。此外,planer将删除任何与其配置的正则表达式不匹配的日志行。出于这个原因,我们早期的planer配置大多具有过于通用的正则表达式——这意味着我们在调试问题时搜索的日志行大多具有消息中的某个位置的参数,而不是拉入params字段。请注意,这意味着我们捕获了我们想要的所有日志,只是没有对它们施加任何有用的结构,这导致我们在生产调试过程中承受了大量的痛苦并损失了宝贵的时间。

我们有哪些选项来改进刨床配置?我们可以:

搜索现有的日志,看看哪些消息中似乎有参数,然后手工制作正则表达式来匹配这些行。检查第三方产品的源代码,并从代码进行的日志调用中撤销正则表达式。

这两个选项都很慢,而且容易出错。因此,我们开始寻找一种可以提高速度和准确性的方法,从而编写更有用的刨床配置。

以编程方式生成刨床配置

Rubix团队部署和支持的大多数第三方软件都是用Golang编写的。Golang的标准库包括go/ast包,它使使用者能够读取和遍历go包的抽象语法树。go AST中的每个节点都有一个具体类型。Golang中的日志调用(或者更一般地说,所有方法调用)采用以下具体类型之一:*ast.CallExpr(callexpression是call expression的缩写)。对于etcd存储库,防御cc及ddos等各种攻击,这些日志调用表达式如下所示:

plog.Printf("在%d完成了计划的压缩(花费了%v)",compactMainRev,time.Since(totalStart))plog.Warningf("%srequest%q,结果为%q,执行时间过长(%v)",prefix,reqStringer.String(),result,d)

利用这些日志调用表达式的已知方法签名(与标准库的fmt.Printf方法相同),我们可以遍历存储库中每个包的AST,当我们找到一个我们认为是日志调用的调用表达式时,根据调用表达式的参数构造一个正则表达式。在伪代码中,这看起来像: