​​​​​​​Micrometer:Java实现可观察性的新方法


OTEL 等较新的标准引入了新的方法来全面查看日志记录、跟踪和指标。

Micrometer,它的可观察性和跟踪方法代表了我正在寻找的下一步的开始。最终将允许开发人员将可观察性结构转变为一流代码公民的抽象。

里是如何使用 Micrometer 在代码中定义可观察性:

@PostMapping("/pets/new")
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) {

  Observation observation = Observation.createNotStarted("create_pet", registry).start();
  try (Observation.Scope scope = observation.openScope()) {
    // only 2 names are valid (low cardinality)
    observation.lowCardinalityKeyValue("pet.type", pet.getType().getName());
    observation.highCardinalityKeyValue("pet.name", pet.getName());

    observation.event(Observation.Event.of("PetCreated"));

    //more logic

    this.owners.save(owner);
    return "redirect:/owners/{ownerId}";
  }


  catch (Exception exception) {

    observation.error(exception);
    throw exception;
  }
  finally {
    observation.stop();
  }

}

让我们剖析一下,我们在上面的例子中看到的:

首先,开发人员可以定义一个观察范围,并突出显示需要跟踪的值。一个必要的步骤是区分倾向于高度独特(此处标记为“高基数”)因此索引和分组效率低下的值,以及那些具有一组离散值的项目,这些值非常适合数据操作稍后在可观察性管道上。

其次,请注意我们如何不需要指定(或知道)任何关于特定可观察性抽象的信息,例如跟踪、跨度、指标等。作为开发人员,我们只需定义我们对观察此特定代码行为方式的兴趣。在幕后,抽象处理了以下内容:

  • 为观察上下文创建跨度
  • 为跟踪值创建两个标签作为跨度的一部分
  • 为观察范围添加多个指标,并将低基数值作为其一部分进行跟踪(例如计时器、计数器)
  • 通过“错误”抽象明确记录任何错误

你可以看到Jaeger中捕获的跟踪结果。

作为开发人员,它将重点转移到意图、工件和事件上,而不是担心底层技术和样板。

如果上面的例子对你来说太冗长了(我喜欢它的明确性),还有一个更简洁的类似 DSL 的接口来实现相同的结果:

@PostMapping("/pets/{petId}/edit")
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {

  return Observation.createNotStarted("update ", registry).observe(()->{

    if (result.hasErrors()) {
      model.put("pet", pet);
      return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
    }

    owner.addPet(pet);
    this.owners.save(owner);
    return "redirect:/owners/{ownerId}";

  });

}


可观察性的外观
Micrometer 的独特之处之一是它旨在成为支持多种可观察性标准(包括 OTEL、Brave 等)的门面。

因此,它的重点更多地放在定义应用程序中使用的词汇、约定和接口上,从代码的角度来看,这可以说是使日志记录有效的更重要的方面。

这是我如何设置 Micrometer 以与 OTEL 一起工作的示例(您可以在此处找到完整的代码):

@Bean
public OpenTelemetry getOpenTelemetry() {
    Resource serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName));

    // [OTel component] SpanExporter is a component that gets called when a span is finished.
    OtlpGrpcSpanExporter otlpExporter =
        OtlpGrpcSpanExporter.builder()
            .setEndpoint(otlpEexporterEndpoint)
            .setTimeout(30, TimeUnit.SECONDS)
            .build();

    // [OTel component] SdkTracerProvider is an SDK implementation for TracerProvider
    SdkTracerProvider sdkTracerProvider =
        SdkTracerProvider.builder()
            .setResource(Resource.getDefault().merge(serviceNameResource))
            .addSpanProcessor(SimpleSpanProcessor.create(otlpExporter))
            .build();

    // [OTel component] The SDK implementation of OpenTelemetry
    OpenTelemetrySdk openTelemetrySdk =
        OpenTelemetrySdk.builder()
            .setTracerProvider(sdkTracerProvider)
            .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader()))
            .build();

    return openTelemetrySdk;
}


此外,我了解到,因为它根本不使用反射或“魔法”。Micrometer 仪器不会遇到其他仪器可能无意中引入软件项目的一些性能问题。


接下来是什么?
我对使用新的、现代的、整体的抽象来实现可观察性、封装日志跟踪和指标的前景感到非常兴奋。这个库是通过@jonatan_ivanov、 Tommy Ludwig (@TommyLudwig)、Marcin Grzejszczak (@MGrzejszczak)和其他真正在创造新颖事物的人的工作而实现的。

与 Micrometer 一起使用时,库中添加了一些内容,这将使它对开发人员来说更加宝贵:

  1. 更简单的无代码配置——通过环境变量指定导出器和其他设置。
  2. 更复杂的参数提取策略——自动捕获数组大小、枚举值或 JSON 长度作为每个请求的一部分。
  3. 更好的日志记录检测 - 隐式地将 TraceId 和其他标签添加到您的日志/结构日志提供程序。

也就是说,Micrometer 是一个成熟的、生产就绪的框架,它可以对您的代码库的可观察性以及您作为开发人员在设计和代码决策中更加数据驱动的能力产生惊人的影响。