[spring-projects/spring-boot]启用提供自定义 OtlpHttpSpanExporter

2024-05-08 972 views
7

这个问题最初是在这里报告的:https://github.com/spring-projects/spring-boot/pull/34508#issuecomment-1557438909,作者:@chicobento:

@jonatan-ivanov 您好,我们在将其集成到我们的项目中时面临一些问题。场景如下:

  • 我们有一个自定义类,它实现SpanExporter接口并在内部包装我们自己的可变对象OtlpHttpSpanExporter
  • 这样,我们需要阻止OtlpAutoConfiguration创建OtlpHttpSpanExporterBean。但是, @ConditionalOnMissingBean 仅基于 OtlpHttpSpanExporter 和 OtlpGrpcSpanExporter bean,这不是我们的情况,因为我们实现了SpanExporter接口并且不能从其OtlpHttpSpanExporter最终的继承

我尝试过的一些可能的问题解决方案:

  • 删除OtlpAutoConfiguration:此行为是由内部库添加的,因此我无法轻松删除OtlpAutoConfiguration而不影响使用我们的库的应用程序
  • 替换SpanProcessor: 因为该OpenTelemetryAutoConfiguration.otelSpanProcessor方法没有@ConditionalOnMissingBean.我无法轻松地将 替换为会忽略Bean 提供的SpanProcessor自定义 BeanOtlpHttpSpanExporterOtlpAutoConfiguration
  • 替换OpenTelemetryAutoConfiguration.otelSdkTracerProvider:这不起作用,因为它将SpanProcessor.otelSpanProcessor始终实例化 BatchSpanProcessor 并启动其线程。我必须关闭它

您对我如何克服这个问题还有其他建议吗?

我想知道当前的工厂方法是否OtlpAutoConfiguration.otlpHttpSpanExporter太具有侵入性,因为它总是提供一个SpanExporterBean,即使应用程序已经定义了自己的 Bean SpanExporter?我们可以把它改成简单的@ConditionalOnMissingBean(SpanExporter.class)吗?

回答

9

您在此处描述的一些行为是有意为之的:我们希望允许定义其他 Bean,同时保留我们提供的默认 Bean SpanExporterSpanProcessor例如:由于您OtlpHttpSpanExporter在类路径上,我们假设您想将其用作导出器,但我们也允许您创建另一个自定义导出器。SpanProcessor类似,请参阅此问题:https://github.com/spring-projects/spring-boot/issues/35560和此评论:https://github.com/spring-projects/spring-boot/pull/35558#问题评论-1552838028

这也是我们不能真正做到这一点的原因:@ConditionalOnMissingBean(SpanExporter.class)这意味着第二个 bean 不是添加而是覆盖。

我确实有几个想法:

  1. 我们可以enabled为 引入一个标志OtlpAutoConfiguration。这有点问题,因为我们已经有一个信号来启用/禁用它,它是类的存在OtlpHttpSpanExporter,但如果我们有一个有效的用例,我们可以重新考虑这一点并添加标志。
  2. 您可以进行自定义SpanProcessor @Primary并关闭 Bean 后处理器中的第二个。
  3. 看起来你想要定制OtlpHttpSpanExporter,但它是故意做的final。您想要添加什么自定义行为OtlpHttpSpanExporter?这种行为可以添加到OtlpHttpSpanExporter自身吗?或者您可以提出一个问题以使其成为OtlpHttpSpanExporter非最终版本吗?
4

SpanProcessor如果我们应该引入一个可以否决将 a添加到 的抽象,我们可以考虑一件事SdkTracerProvider。就像是:

@Bean
    @ConditionalOnMissingBean
    SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider<SpanProcessor> spanProcessors,
            Sampler sampler, ObjectProvider<SdkTracerProviderBuilderCustomizer> customizers,
            SpanProcessorVetoer spanProcessorVetoer) {
        String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
        SdkTracerProviderBuilder builder = SdkTracerProvider.builder()
            .setSampler(sampler)
            .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
        spanProcessors.orderedStream().forEach((spanProcessor) -> {
            if (!spanProcessorVetoer.veto(spanProcessor)) {
                builder.addSpanProcessor(spanProcessor);
            }
        });
        customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
        return builder.build();
    }

    /**
     * Can veto against adding a {@link SpanProcessor} to the {@link SdkTracerProvider}.
     */
    interface SpanProcessorVetoer {

        /**
         * Returns {@code true} if veto against the given {@link SpanProcessor}, meaning
         * that the span processor won't be added to the {@link SdkTracerProvider}.
         * @param spanProcessor span processor which is under vote
         * @return {@code true} if vetoed against the given span processor, {@code false}
         * otherwise
         */
        boolean veto(SpanProcessor spanProcessor);

    }

SpanProcessor这将在更抽象的层面上解决多个 s 的问题。

8

嗨@jonatan-ivanov,

感谢您及时的回复。

关于选项3,我们重写bean的主要原因OtlpHttpSpanExporter是我们需要支持自定义TLS证书和证书刷新场景。因此,在我们的解决方案中,我们创建了一个MutableSpanProcessor封装 a 的对象OtlpHttpSpanExporter,并在颁发新证书时刷新内部实例。开放遥测已经通过改进对自定义证书的支持(例如项目https://github.com/open-telemetry/opentelemetry-java/issues/5211)来提供帮助,并且我认为扩展OtlpHttpSpanExporter不会有太大帮助。

现在关于选项 2(和 @mhalbritter 提案变体),缺点是线程BatchSpanProcessor被创建/启动然后关闭 - 这不太好。

因此,我认为我们只剩下选项 1,因为 BWC 关注SpanProcessors 的可加性行为。

0

我认为对我来说能够延长OtlpHttpSpanExporter是有道理的,为什么你认为这没有帮助? (不过我不确定 OTel 会这样做。)我认为选项 2 更像是一种解决方法,直到由于需要关闭处理器而无法修复该问题。

@mhalbritter 对于引入启用标志(也许对于 zipkin 也是如此)您有何看法?

6

我不太喜欢那些启用的标志,但如果我们找不到更好的解决方案,也许这就是我们必须吞下的药丸。也许团队中的其他人有更好的想法,让我们在下次会议中讨论。

9

我认为对我来说能够扩展 OtlpHttpSpanExporter 是有意义的,为什么你认为它没有帮助? (不过我不确定 OTel 会这样做。)

我同意,它很有用,只是很难找到一个具体的用例来证明这一点,并且没有太大的期望,因为 OTel 对他们的 API 表面非常严格。

5

我们今天讨论了这个问题,我们正在考虑的一个想法是删除默认OtlpProperties.endpoint值。这意味着如果用户想要 HTTP 导出,他们需要添加management.otlp.tracing.endpoint=http://localhost:4318/v1/traces到他们的属性中。

这会给我们一个强烈的信号,告诉我们何时自动配置OtlpHttpSpanExporter.

这种方法有一些缺点。 1)这是一个突破性的改变。 2) 它使配置变得更加冗长。

2

同时,您可以尝试添加spring.autoconfigure.exclude=org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfigurationapplication.properties使用@SpringBootApplication(exclude=OtlpAutoConfiguration.class)来禁用我们的自动配置。

8

嗨@philwebb,很公平。

你的意思是你会添加一个@ConditionalOnProperty(management.otlp.tracing.endpoint)?既然这无论如何都是一个突破性的改变,为什么不添加呢@ConditionalOnMissingBean(SpanExporter.class)

同时,您可以尝试将 spring.autoconfigure.exclude=org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration 添加到您的 application.properties 或使用 @SpringBootApplication(exclude=OtlpAutoConfiguration.class) 禁用我们的自动配置。

是的,我试图避免这种情况,因为我们SpanExporter在共享库 jar 中提供了自定义 bean,所以我想避免对应用程序的影响,但在这一点上,这似乎是最好的选择之一(或者关闭处理器)。

非常感谢大家的快速回复。

3

顺便说一句,对于我们的特定部署,OTLP 收集器是远程运行的,因此即使我们可以坚持使用 stock OtlpHttpSpanExporter,我们仍然必须配置端点。

5

management.otlp.tracing.endpoint仅当设置时,自动配置才会激活。我已经删除了该属性的默认设置。

1

如果端点属性成为自动配置导出器的强制要求,那么在我看来,自动配置不应仅限于仅支持 HTTP 导出器。

0

您也想查看 的自动配置吗OtlpGrpcSpanExporter

0

好吧,由于现在必须提供端点 URL,因此它可以用于确定用户想要配置哪种类型的导出器,并且还支持 gRPC 选项。