返回介绍

9.2 日志聚合与 Spring Cloud Sleuth

发布于 2025-04-22 21:54:09 字数 11126 浏览 0 评论 0 收藏

在大型的微服务环境中(特别是在云环境中),日志记录数据是调试问题的关键工具。但是,因为基于微服务的应用程序的功能被分解为小型的细粒度的服务,并且单个服务类型可以有多个服务实例,所以尝试绑定来自多个服务的日志数据以解决用户的问题可能非常困难。试图跨多个服务器调试问题的开发人员通常不得不尝试以下操作。

  • 登录到多个服务器以检查每个服务器上的日志。这是一项非常费力的任务,尤其是在所涉及的服务具有不同的事务量,导致日志以不同的速率滚动的时候。
  • 编写尝试解析日志并标识相关的日志条目的本地查询脚本。由于每个查询可能不同,因此开发人员经常会遇到大量的自定义脚本,用于从日志中查询数据。
  • 延长停止服务的进程的恢复,因为开发人员需要备份驻留在服务器上的日志。如果托管服务的服务器彻底崩溃,则日志通常会丢失。

上面列出的每一个问题都是我遇到过的实际问题。在分布式服务器上调试问题是一件很糟糕的工作,并且常常会明显增加识别和解决问题所需的时间。

一种更好的方法是,将所有服务实例的日志实时流到一个集中的聚合点,在那里可以对日志数据进行索引并进行搜索。图 9-3 在概念层面展示了这种“统一”的日志记录架构是如何工作的。

图 9-3 将聚合日志与跨服务日志条目的唯一事务 ID 结合,更易于管理分布式事务的调试

幸运的是,有多个开源产品和商业产品可以帮助我们实现前面描述的日志记录架构。此外,还存在多个实现模型,可供开发人员在内部部署、本地管理或者基于云的解决方案之间进行选择。表 9-1 总结了可用于日志记录基础设施的几个选择。

表 9-1 与 Spring Boot 组合使用的日志聚合方案的选项

产品名称

实现模式

备 注

Elasticsearch,
Logstash,
Kibana(ELK)

开源
商业
通常实施于内部部署

通用搜索引擎
可以通过 ELK 技术栈进行日志聚合
需要最多的手工操作

Graylog

开源
商业
内部部署

设计为在内部安装的开源平台

Splunk

仅限于商业
内部部署和基于云

最古老且最全面的日志管理和聚合工具
最初是内部部署的解决方案,但后来提供了云服务

Sumo Logic

免费增值模式
商业
基于云

免费增值模式/分层定价模型
仅作为云服务运行
需要用公司的工作账户去注册(不能是 Gmail 或 Yahoo 账户)

Papertrail

免费增值模式
商业
基于云

免费增值模式/分层定价模型
仅作为云服务运行

很难从上面选出哪个是最好的。每个组织都各不相同,并且有不同的需求。

在本章中,我们将以 Papertrail 为例,介绍如何将 Spring Cloud Sleuth 支持的日志集成到统一的日志记录平台中。选择 Papertrail 出于以下 3 个原因。

(1)它有一个免费增值模式,可以注册一个免费的账户。

(2)它非常容易创建,特别是和 Docker 这样的容器运行时工作。

(3)它是基于云的。虽然我认为良好的日志基础设施对于微服务应用程序是至关重要的,但我不认为大多数组织都有时间或技术才能去正确地创建和管理一个日志记录平台。

9.2.1 Spring Cloud Sleuth 与 Papertrail 集成实战

在图 9-3 中,我们看到了一个通用的统一日志架构。现在我们来看看如何使用 Spring Cloud Sleuth 和 Papertrail 来实现相同的架构。

为了让 Papertrail 与我们的环境一起工作,我们必须采取以下措施。

(1)创建一个 Papertrail 账户并配置一个 Papertrail syslog 连接器。

(2)定义一个 Logspout Docker 容器,以从所有 Docker 容器捕获标准输出。

(3)通过基于来自 Spring Cloud Sleuth 的关联 ID 发出查询来测试这一实现。

图 9-4 展示了这一实现的最终状态,以及 Spring Cloud Sleuth 和 Papertrail 如何与解决方案融合。

图 9-4 使用原生 Docker 功能、Logspout 和 Papertrail 可以快速实现统一的日志记录架构

9.2.2 创建 Papertrail 账户并配置 syslog 连接器

我们将从创建一个 Papertrail 账号开始。要开始使用 PaperTrail,应访问 https://papertrailapp.com 并点击绿色的“Start Logging-Free Plan”按钮。图 9-5 展示了这个界面。

图 9-5 首先,在 Papertrail 上创建一个账户

Papertrail 不需要大量的信息去启动,只需要一个有效的电子邮箱地址即可。填写完账户信息后,将出现一个界面,用于创建记录数据的第一个系统。图 9-6 展示了这个界面。

图 9-6 接下来,选择如何将日志数据发送到 Papertrail

在默认情况下,Papertrail 允许开发人员通过 Syslog 调用向它发送日志数据。Syslog 是源于 UNIX 的日志消息传递格式,它允许通过 TCP 和 UDP 发送日志消息。Papertrail 将自动定义一个 Syslog 端口,可以使用它来写入日志消息。在本章的讨论中,我们将使用这个默认端口。图 9-7 展示了 syslog 连接字符串,在点击图 9-6 所示的“Add your first system”按钮时,它将自动生成。

图 9-7 Papertrail 使用 Syslog 作为向它发送数据的机制之一

到目前为止,我们已经设置完 Papertrail。接下来,我们必须配置 Docker 环境,以便将运行服务的每个容器的输出捕获到图 9-7 中定义的远程 syslog 端点。

注意

图 9-7 中的连接字符串是我的账户特有的。读者需要确保自己使用了 Papertrail 为自己生成的连接字符串,或者通过 Papertrail Settings→Log destinations 菜单选项来定义一个连接字符串。

9.2.3 将 Docker 输出重定向到 Papertrail

通常情况下,如果在虚拟机中运行每个服务,那么必须配置每个服务的日志记录配置,以便将它的日志信息发送到一个远程 syslog 端点(如通过 Papertrail 公开的那个端点)。

幸运的是,Docker 让从物理机或虚拟机上运行的 Docker 容器中捕获所有输出变得非常容易。Docker 守护进程通过一个名为 docker.sock 的 Unix 套接字来与所有 Docker 容器进行通信。在 Docker 所在的服务器上,每个容器都可以连接到 docker.sock ,并接收由该服务器上运行的所有其他容器生成的所有消息。用最简单的术语来说, docker.sock 就像一个管道,容器可以插入其中,并捕获 Docker 运行时环境中进行的全部活动,这些 Docker 运行时环境是在 Docker 守护进程运行的虚拟服务器上的。

我们将使用一个名为 Logspout 的“Docker 化”软件,它会监听 docker.sock 套接字,然后捕获在 Docker 运行时生成的任意标准输出消息,并将它们重定向输出到远程 syslog(Papertrail)。要建立 Logspout 容器,必须要向 docker-compose.yml 文件添加一个条目,它用于启动本章代码示例使用的所有 Docker 容器。我们需要修改 docker/common/docker-compose.yml 文件以添加以下条目:

logspout:
  image: gliderlabs/logspout
  command: syslog://logs5.papertrailapp.com:21218
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock

注意

在上面的代码片段中,读者需要将 command 属性中的值替换为 Papertrail 提供的值。如果读者使用上述 Logspout 代码片段,Logspout 容器会很乐意将日志条目写入我的 Papertrail 账户。

现在,当读者启动本章中 Docker 环境时,所有发送到容器标准输出的数据都将发送到 Papertrail。在启动完第 9 章的 Docker 示例之后,读者通过登录自己的 Papertrail 账户,然后点击界面右上角的“Events”按钮,就可以看到数据都发送到 Papertrail。

图 9-8 展示了发送到 Papertrail 的数据的示例。

图 9-8 在定义了 Logspout Docker 容器的情况下,写入每个容器标准输出的数据将被发送到 Papertrail

为什么不使用 Docker 日志驱动程序

Docker 1.6 及更高版本允许开发人员定义其他日志驱动程序,以记录在每个容器中写入的 stdout/stderr 消息。其中一个日志记录驱动程序是 syslog 驱动程序,它可用于将消息写入远程 syslog 监听器。

为什么我会选择 Logspout 而不是使用标准的 Docker 日志驱动程序?主要原因是灵活性。Logspout 提供了定制日志数据发送到日志聚合平台的功能。Logspout 提供的功能有以下几个。

  • 能够一次将日志数据发送到多个端点 。许多公司都希望将自己的日志数据发送到一个日志聚合平台,同时还需要安全监控工具,用于监控生成的日志中的敏感数据。
  • 一个集中的位置过滤哪些容器将发送它们的日志数据 。使用 Docker 驱动程序,开发人员需要在 docker-compose.yml 文件中为每个容器手动设置日志驱动程序,而 Logspout 则允许开发人员在集中式配置中定义特定容器甚至特定字符串模式的过滤器。
  • 自定义 HTTP 路由,允许应用程序通过特定的 HTTP 端点来写入日志信息 。这个特性允许开发人员完成一些事情,例如将特定的日志消息写入特定的下游日志聚合平台。举个例子,开发人员可能会将一般的日志消息从 stdout/stderr 转到 Papertrail,与此同时,可能会希望将特定应用程序审核信息发送到内部的 Elasticsearch 服务器。
  • 与 syslog 以外的协议集成 。Logspout 可以通过 UDP 和 TCP 协议发送消息。此外,Logspout 还具有第三方模块,可以将 Docker 的 stdout/stderr 整合到 Elasticsearch 中。

9.2.4 在 Papertrail 中搜索 Spring Cloud Sleuth 的跟踪 ID

现在,日志正在流向 Papertrail,我们可以真正开始感激 Spring Cloud Sleuth 将跟踪 ID 添加到所有日志条目中。要查询与单个事务相关的所有日志条目,只需在 Papertrail 的事件界面的查询框中输入跟踪 ID 并进行查询即可。图 9-9 展示了如何使用在 9.1.2 节中使用的 Spring Cloud Sleuth 跟踪 ID a9e3e1786b74d302 来执行查询。

图 9-9 跟踪 ID 可用于筛选与单个事务相关的所有日志条目

统一日志记录和对平凡的赞美

不要低估拥有一个统一的日志架构和服务关联策略的重要性。这似乎是一项平凡的任务,但在我撰写这一章的时候,我使用了类似于 Papertrail 的日志聚合工具为我正在开发的一个项目跟踪 3 个不同服务之间的竞态条件。事实表明,这个竞态条件已经存在了一年多时间了,但处于竞态条件下的服务一直运行良好,直到我们增加了一点儿负载并加入另一个参与者才导致问题出现。

我们用了 1.5 周的时间进行日志查询,并遍历了几十个独特场景的跟踪输出之后才发现了这个问题。如果没有聚合的日志记录平台,我们也就不会发现这个问题。这次经历再次肯定了以下几件事。

(1) 确保在服务开发的早期定义和实现日志策略 ——一旦项目开展起来,实现日志基础设施会是一项冗长的、有时很困难的工作并且还会耗费大量时间。

(2) 日志记录是微服务基础设施的一个关键部分 ——在实现你自己的日志记录方案或是尝试实现内部部署的日志记录方案之前,一定要再三考虑清楚。花在基于云的日志记录平台上的钱是值得的。

(3) 学习日志记录工具 ——几乎每个日志平台都有一个查询语言来查询合并的日志。日志是信息和度量的一个极其重要的来源。它们本质上是另一种类型的数据库,花在学习查询上的时间将会带来巨大的回报。

9.2.5 使用 Zuul 将关联 ID 添加到 HTTP 响应

如果读者检查使用 Spring Cloud Sleuth 进行服务调用所返回的 HTTP 响应,永远不会看到在调用中使用的跟踪 ID 在 HTTP 响应首部中返回。通过查阅 Spring Cloud Sleuth 的文档,就会得知 Spring Cloud Sleuth 团队认为返回的跟踪数据可能是一个潜在的安全问题(尽管他们没有明确列出理由)。

然而,我发现,在调试问题时,在 HTTP 响应中返回关联 ID 或跟踪 ID 是非常重要的。Spring Cloud Sleuth 允许开发人员使用其跟踪 ID 和跨度 ID“装饰”HTTP 响应信息。然而,这种做法涉及编写 3 个类并注入两个定制的 Spring bean。如果读者想采取这种方法,可以查阅 Spring Cloud Sleuth 文档。一个更简单的解决方案是编写一个将在 HTTP 响应中注入跟踪 ID 的 Zuul 后置过滤器。

在第 6 章介绍 Zuul API 网关时,我们看到了如何构建一个 Zuul 后置响应过滤器,将生成的用于服务的关联 ID 添加到调用者返回的 HTTP 响应中。我们现在要修改这个过滤器以添加 Spring Cloud Sleuth 首部。

要创建 Zuul 响应过滤器,需要将 JAR 依赖项 spring-cloud-starter-sleuth 添加到 Zuul 服务器的 pom.xml 文件中。 spring-cloud-starter-sleuth 依赖项用于告诉 Spring Cloud Sleuth,希望 Zuul 参与 Spring Cloud 跟踪。在本章稍后介绍 Zipkin 时,读者会看到 Zuul 服务将成为所有服务调用中的第一个调用。

对于第 9 章,这个文件可以在 zuulsvr/pom.xml 中找到。代码清单 9-1 展示了这些依赖项。

代码清单 9-1 将 Spring Cloud Sleuth 添加到 Zuul

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-sleuth</artifactId>  ⇽--- 向 Zuul 添加 spring-cloud-starter-sleuth 会让在 Zuul 中调用的每个服务生成一个跟踪 ID
</dependency>

添加完新的依赖项,实际的 Zuul 后置过滤器就很容易实现了。代码清单 9-2 展示了用于构建 Zuul 过滤器的源代码。该代码在 zuulsvr/src/main/java/com/thoughtmechanix/zuulsvr/filters/ ResponseFilter.java 中。

代码清单 9-2 通过 Zuul 后置过滤器添加 Spring Cloud Sleuth 的跟踪 ID

package com.thoughtmechanix.zuulsvr.filters;

// 为了简洁,省略了其他 import 语句
import org.springframework.cloud.sleuth.Tracer;

@Component
public class ResponseFilter extends ZuulFilter {
    private static final int     FILTER_ORDER=1;
    private static final boolean  SHOULD_FILTER=true;
    private static final Logger logger = LoggerFactory.getLogger(ResponseFilter.class);

    @Autowired  ⇽--- Tracer 类是访问跟踪 ID 和跨度 ID 信息的入口点
    Tracer tracer;

    @Override
    public String filterType() {return "post";}

    @Override
    public int filterOrder() {return FILTER_ORDER;}

    @Override
    public boolean shouldFilter() {return SHOULD_FILTER;}

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.getResponse().addHeader("tmx-correlation-id",
        →  tracer.getCurrentSpan().traceIdString());  ⇽--- 添加新 HTTP 响应首部 tmx-correlation-id,它包含 Spring Cloud Sleuth 的跟踪 ID

        return null;
    }
}

因为 Zuul 现在已经启用了 Spring Cloud Sleuth,所以可以通过自动装配 Tracer 类到 ResponseFilterResponseFilter 中访问跟踪信息。 Tracer 类可用于访问正在执行的当前 Spring Cloud Sleuth 跟踪信息。 tracer.getCurrentSpan().traceIdString() 方法以字符串的形式检索当前正在进行的事务的跟踪 ID。

将跟踪 ID 添加到通过 Zuul 的传出 HTTP 响应是很简单的。这一步骤通过调用以下代码来完成:

RequestContext ctx = RequestContext.getCurrentContext();
ctx.getResponse().addHeader("tmx-correlation-id", 
→  tracer.getCurrentSpan().traceIdString());

有了这段代码,如果通过 Zuul 网关调用了一个 EagleEye 微服务,那么应该会得到一个名为 tmx-correlation-id 的 HTTP 响应首部。图 9-10 展示了调用 GET http://localhost:5555/api/licensing/v1/organizations/e254f8c-c442-4ebe-a82a-
e2fc1d1ff7-8a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a 的结果。

图 9-10 随着 Spring Cloud Sleuth 的跟踪 ID 的返回,可以轻松地向 Papertrail 查询日志

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。