返回介绍

7.4.3 添加自定义度量信息

发布于 2025-04-21 21:10:11 字数 5756 浏览 0 评论 0 收藏

在 7.1.2 节中,你看到了如何从/metrics 端点获得运行中应用程序的内部度量信息,包括内存、垃圾回收和线程信息。这些都是非常有用且信息量很大的度量值,但你可能还想定义自己的度量,用来捕获应用程序中的特定信息。

比方说,我们想要知道用户往阅读列表里保存了多少次图书,最简单的方法就是在每次调用 ReadingListControlleraddToReadingList() 方法时增加计数器值。计数器很容易实现,但这个不断变化的总计值如何同/metrics 端点发布的度量信息一起发布出来呢?

再假设我们想要获得最后保存图书的时间戳。时间戳可以通过调用 System.currentTimeMillis() 来获取,但如何在/metrics 端点里报告该时间戳呢?

实际上,自动配置允许 Actuator 创建 CounterService 的实例,并将其注册为 Spring 的应用程序上下文中的 Bean。 CounterService 这个接口里定义了三个方法,分别用来增加、减少或重置特定名称的度量值,代码如下:

package org.springframework.boot.actuate.metrics;

public interface CounterService {
  void increment(String metricName);
  void decrement(String metricName);
  void reset(String metricName);
}

Actuator 的自动配置还会配置一个 GaugeService 类型的 Bean。该接口与 CounterService 类似,能将某个值记录到特定名称的度量值里。 GaugeService 看起来是这样的:

package org.springframework.boot.actuate.metrics;

public interface GaugeService {
  void submit(String metricName, double value);
}

你无需实现这些接口。Spring Boot 已经提供了两者的实现。我们所要做的就是把它们的实例注入所需的 Bean,在适当的时候调用其中的方法,更新想要的度量值。

针对上文提到的需求,我们需要把 CounterServiceGaugeService Bean 注入 ReadingListController ,然后在 addToReadingList() 方法里调用其中的方法。代码清单 7-9 是 ReadingListController 里的相关变动:

代码清单 7-9 使用注入的 CounterServiceGaugeService

@Controller
@RequestMapping("/")
@ConfigurationProperties("amazon")
public class ReadingListController {

  ...

  private CounterService counterService;

  @Autowired
  public ReadingListController(
      ReadingListRepository readingListRepository,
      AmazonProperties amazonProperties,
      CounterService counterService,
      GaugeService gaugeService) {       ←---注入 CounterService 和 GaugeService
    this.readingListRepository = readingListRepository;
    this.amazonProperties = amazonProperties;
    this.counterService = counterService;
    this.gaugeService = gaugeService;
  }

  ...

  @RequestMapping(method=RequestMethod.POST)
  public String addToReadingList(Reader reader, Book book) {
    book.setReader(reader);
    readingListRepository.save(book);

    counterService.increment("books.saved");     ←---增加 books.saved 的值

    gaugeService.submit(
            "books.last.saved", System.currentTimeMillis()); ←---记录 books.last.saved 的值
    return "redirect:/";
  }

}

修改后的 ReadingListController 使用了自动织入机制,通过控制器的构造方法注入 CounterServiceGaugeService ,随后把它们保存在实例变量里。此后, addToReadingList() 方法每次处理请求时都会调用 counterService.increment ("books.saved")gaugeService.submit("books.last.saved") 来调整度量值。

尽管 CounterServiceGaugeService 用起来很简单,但还是有一些度量值很难通过增加计数器或记录指标值来捕获。对于那些情况,我们可以实现 PublicMetrics 接口,提供自己需要的度量信息。该接口定义了一个 metrics() 方法,返回一个 Metric 对象的集合:

package org.springframework.boot.actuate.endpoint;

public interface PublicMetrics {
  Collection<Metric<?>> metrics();
}

为了解 PublicMetrics 的使用方法,这里假设我们想报告一些源自 Spring 应用程序上下文的度量值 - 应用程序上下文启动的时间、Bean 及 Bean 定义的数量,这些都包含进来会很有意思。顺便再报告一下添加了 @Controller 注解的 Bean 的数量。代码清单 7-10 给出了相应 PublicMetrics 实现的代码。

代码清单 7-10 发布自定义度量信息

package readinglist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

@Component
public class ApplicationContextMetrics implements PublicMetrics {

  private ApplicationContext context;

  @Autowired
  public ApplicationContextMetrics(ApplicationContext context) {
    this.context = context;
  }

  @Override
  public Collection<Metric<?>> metrics() {
    List<Metric<?>> metrics = new ArrayList<Metric<?>>();
    metrics.add(new Metric<Long>("spring.context.startup-date",   ←---记录启动时间
        context.getStartupDate()));

    metrics.add(new Metric<Integer>("spring.beans.definitions",   ←---记录 Bean 定义数量
        context.getBeanDefinitionCount()));

    metrics.add(new Metric<Integer>("spring.beans",
        context.getBeanNamesForType(Object.class).length));      ←---记录 Bean 数量

    metrics.add(new Metric<Integer>("spring.controllers",
        context.getBeanNamesForAnnotation(Controller.class).length));   ←---记录控制器类型的 Bean 数量

    return metrics;
  }

}

Actuator 会调用 metrics() 方法,收集 ApplicationContextMetrics 提供的度量信息。该方法调用了所注入的 ApplicationContext 上的方法,获取我们想要报告为度量的数量。每个度量值都会创建一个 Metrics 实例,指定度量的名称和值,将其加入要返回的列表。

创建 ApplicationContextMetrics ,并在 ReadingListController 里使用 CounterServiceGaugeService 之后,我们可以在/metrics 端点的响应中找到如下条目:

{
  ...
  spring.context.startup-date: 1429398980443,
  spring.beans.definitions: 261,
  spring.beans: 272,
  spring.controllers: 2,
  books.count: 1,
  gauge.books.save.time: 1429399793260,
  ...
}

当然,这些度量的实际值会根据添加了多少书、何时启动应用程序及何时保存最后一本书而发生变化。在这个例子里,你一定会好奇为什么 spring.controllers 是 2。因为这里算上了 ReadingListController 以及 Spring Boot 提供的 BasicErrorController

发布评论

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