add dubbo-spring-boot-observability-starter (#11579)

* add dubbo-spring-boot-observability-starter

* add License and fix codestyle

* Add License

* Add dubbo-spring-boot-observability-starter to dubbo-dependencies-all

* Changes following review

* Changes following review

* Polish pom

* Changes following review

* Add dubbo-metrics-default in dubbo-spring-boot-observability-starter

* Remove author tag

* Polish pom
This commit is contained in:
ShenFeng312 2023-03-02 06:37:06 +08:00 committed by GitHub
parent dc74f131a7
commit 34981c9606
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1318 additions and 437 deletions

View File

@ -134,15 +134,16 @@
</dependency>
<!-- Observabililty -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-observability-starter</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -1,215 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.springboot.demo.consumer;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.otel.bridge.ArrayListSpanProcessor;
import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelPropagator;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.propagation.Propagator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collections;
import static io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn;
@Configuration
public class ObservationConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
@javax.annotation.Resource
private ApplicationModel applicationModel;
@Bean
ObservationRegistry observationRegistry() {
ObservationRegistry observationRegistry = ObservationRegistry.create();
applicationModel.getBeanFactory().registerBean(observationRegistry);
return observationRegistry;
}
@Bean
MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
@Bean
ArrayListSpanProcessor spanExporter() {
return new ArrayListSpanProcessor();
}
@Bean
SdkTracerProvider sdkTracerProvider(Environment environment) {
String applicationName = environment.getProperty("dubbo.application.name", DEFAULT_APPLICATION_NAME);
return SdkTracerProvider.builder().setSampler(alwaysOn())
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter()).build())
.setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)))
.build();
}
@Bean
ContextPropagators contextPropagators() {
return ContextPropagators.create(B3Propagator.injectingSingleHeader());
}
@Bean
OpenTelemetrySdk openTelemetrySdk(SdkTracerProvider sdkTracerProvider) {
return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider)
.setPropagators(contextPropagators()).build();
}
@Bean
io.opentelemetry.api.trace.Tracer otelTracer(OpenTelemetrySdk openTelemetrySdk) {
return openTelemetrySdk.getTracerProvider()
.get("io.micrometer.micrometer-tracing");
}
@Bean
OtelCurrentTraceContext otelCurrentTraceContext() {
return new OtelCurrentTraceContext();
}
Slf4JEventListener slf4JEventListener() {
return new Slf4JEventListener();
}
Slf4JBaggageEventListener slf4JBaggageEventListener() {
return new Slf4JBaggageEventListener(Collections.emptyList());
}
@Bean
OtelTracer tracer(io.opentelemetry.api.trace.Tracer otelTracer, OtelCurrentTraceContext otelCurrentTraceContext) {
Slf4JEventListener slf4JEventListener = slf4JEventListener();
Slf4JBaggageEventListener slf4JBaggageEventListener = slf4JBaggageEventListener();
OtelTracer.EventPublisher eventPublisher = event -> {
slf4JEventListener.onEvent(event);
slf4JBaggageEventListener.onEvent(event);
};
ContextStorage.addWrapper(new EventPublishingContextWrapper(eventPublisher));
return new OtelTracer(otelTracer, otelCurrentTraceContext, eventPublisher, new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList()));
}
@Bean
Propagator propagator(io.opentelemetry.api.trace.Tracer otelTracer) {
return new OtelPropagator(contextPropagators(), otelTracer);
}
@Bean
ObservationHandlerRegistrar observationHandlerRegistrar(ObservationRegistry observationRegistry, OtelTracer tracer, Propagator propagator, MeterRegistry meterRegistry) {
return new ObservationHandlerRegistrar(observationRegistry, tracer, propagator, meterRegistry);
}
@Bean
MetricsDumper metricsDumper(MeterRegistry meterRegistry) {
return new MetricsDumper(meterRegistry);
}
@Bean
TracesDumper tracesDumper(ArrayListSpanProcessor arrayListSpanProcessor) {
return new TracesDumper(arrayListSpanProcessor);
}
static class ObservationHandlerRegistrar {
private final ObservationRegistry observationRegistry;
private final Tracer tracer;
private final Propagator propagator;
private final MeterRegistry meterRegistry;
ObservationHandlerRegistrar(ObservationRegistry observationRegistry, Tracer tracer, Propagator propagator, MeterRegistry meterRegistry) {
this.observationRegistry = observationRegistry;
this.tracer = tracer;
this.propagator = propagator;
this.meterRegistry = meterRegistry;
}
@PostConstruct
void setup() {
observationRegistry.observationConfig().observationHandler(new TracingAwareMeterObservationHandler<>(new DefaultMeterObservationHandler(meterRegistry), tracer));
observationRegistry.observationConfig()
.observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(new PropagatingReceiverTracingObservationHandler<>(tracer, propagator), new PropagatingSenderTracingObservationHandler<>(tracer, propagator), new DefaultTracingObservationHandler(tracer)));
}
}
static class MetricsDumper {
private final MeterRegistry meterRegistry;
MetricsDumper(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@PreDestroy
void dumpMetrics() {
System.out.println("==== METRICS ====");
this.meterRegistry.getMeters().forEach(meter -> System.out.println(" - Metric type \t[" + meter.getId().getType() + "],\tname [" + meter.getId().getName() + "],\ttags " + meter.getId().getTags() + ",\tmeasurements " + meter.measure()));
System.out.println("=================");
}
}
static class TracesDumper {
private final ArrayListSpanProcessor arrayListSpanProcessor;
TracesDumper(ArrayListSpanProcessor arrayListSpanProcessor) {
this.arrayListSpanProcessor = arrayListSpanProcessor;
}
@PreDestroy
void dumpTraces() {
System.out.println("==== TRACES ====");
this.arrayListSpanProcessor.spans().forEach(System.out::println);
System.out.println("=================");
}
}
}

View File

@ -133,14 +133,14 @@
</dependency>
<!-- Observability -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-observability-starter</artifactId>
</dependency>
</dependencies>

View File

@ -1,214 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.springboot.demo.provider;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.otel.bridge.ArrayListSpanProcessor;
import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelPropagator;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.propagation.Propagator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collections;
import static io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn;
@Configuration
public class ObservationConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
@javax.annotation.Resource
private ApplicationModel applicationModel;
@Bean
ObservationRegistry observationRegistry() {
ObservationRegistry observationRegistry = ObservationRegistry.create();
applicationModel.getBeanFactory().registerBean(observationRegistry);
return observationRegistry;
}
@Bean
MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
@Bean
ArrayListSpanProcessor spanExporter() {
return new ArrayListSpanProcessor();
}
@Bean
SdkTracerProvider sdkTracerProvider(Environment environment) {
String applicationName = environment.getProperty("dubbo.application.name", DEFAULT_APPLICATION_NAME);
return SdkTracerProvider.builder().setSampler(alwaysOn())
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter()).build())
.setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)))
.build();
}
@Bean
ContextPropagators contextPropagators() {
return ContextPropagators.create(B3Propagator.injectingSingleHeader());
}
@Bean
OpenTelemetrySdk openTelemetrySdk(SdkTracerProvider sdkTracerProvider) {
return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider)
.setPropagators(contextPropagators()).build();
}
@Bean
io.opentelemetry.api.trace.Tracer otelTracer(OpenTelemetrySdk openTelemetrySdk) {
return openTelemetrySdk.getTracerProvider()
.get("io.micrometer.micrometer-tracing");
}
@Bean
OtelCurrentTraceContext otelCurrentTraceContext() {
return new OtelCurrentTraceContext();
}
Slf4JEventListener slf4JEventListener() {
return new Slf4JEventListener();
}
Slf4JBaggageEventListener slf4JBaggageEventListener() {
return new Slf4JBaggageEventListener(Collections.emptyList());
}
@Bean
OtelTracer tracer(io.opentelemetry.api.trace.Tracer otelTracer, OtelCurrentTraceContext otelCurrentTraceContext) {
Slf4JEventListener slf4JEventListener = slf4JEventListener();
Slf4JBaggageEventListener slf4JBaggageEventListener = slf4JBaggageEventListener();
OtelTracer.EventPublisher eventPublisher = event -> {
slf4JEventListener.onEvent(event);
slf4JBaggageEventListener.onEvent(event);
};
ContextStorage.addWrapper(new EventPublishingContextWrapper(eventPublisher));
return new OtelTracer(otelTracer, otelCurrentTraceContext, eventPublisher, new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList()));
}
@Bean
Propagator propagator(io.opentelemetry.api.trace.Tracer otelTracer) {
return new OtelPropagator(contextPropagators(), otelTracer);
}
@Bean
ObservationHandlerRegistrar observationHandlerRegistrar(ObservationRegistry observationRegistry, OtelTracer tracer, Propagator propagator, MeterRegistry meterRegistry) {
return new ObservationHandlerRegistrar(observationRegistry, tracer, propagator, meterRegistry);
}
@Bean
MetricsDumper metricsDumper(MeterRegistry meterRegistry) {
return new MetricsDumper(meterRegistry);
}
@Bean
TracesDumper tracesDumper(ArrayListSpanProcessor arrayListSpanProcessor) {
return new TracesDumper(arrayListSpanProcessor);
}
static class ObservationHandlerRegistrar {
private final ObservationRegistry observationRegistry;
private final Tracer tracer;
private final Propagator propagator;
private final MeterRegistry meterRegistry;
ObservationHandlerRegistrar(ObservationRegistry observationRegistry, Tracer tracer, Propagator propagator, MeterRegistry meterRegistry) {
this.observationRegistry = observationRegistry;
this.tracer = tracer;
this.propagator = propagator;
this.meterRegistry = meterRegistry;
}
@PostConstruct
void setup() {
observationRegistry.observationConfig().observationHandler(new TracingAwareMeterObservationHandler<>(new DefaultMeterObservationHandler(meterRegistry), tracer));
observationRegistry.observationConfig()
.observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(new PropagatingReceiverTracingObservationHandler<>(tracer, propagator), new PropagatingSenderTracingObservationHandler<>(tracer, propagator), new DefaultTracingObservationHandler(tracer)));
}
}
static class MetricsDumper {
private final MeterRegistry meterRegistry;
MetricsDumper(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@PreDestroy
void dumpMetrics() {
System.out.println("==== METRICS ====");
this.meterRegistry.getMeters().forEach(meter -> System.out.println(" - Metric type \t[" + meter.getId().getType() + "],\tname [" + meter.getId().getName() + "],\ttags " + meter.getId().getTags() + ",\tmeasurements " + meter.measure()));
System.out.println("=================");
}
}
static class TracesDumper {
private final ArrayListSpanProcessor arrayListSpanProcessor;
TracesDumper(ArrayListSpanProcessor arrayListSpanProcessor) {
this.arrayListSpanProcessor = arrayListSpanProcessor;
}
@PreDestroy
void dumpTraces() {
System.out.println("==== TRACES ====");
this.arrayListSpanProcessor.spans().forEach(System.out::println);
System.out.println("=================");
}
}
}

View File

@ -36,7 +36,27 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<skip_maven_deploy>true</skip_maven_deploy>
<spring-boot.version>2.7.8</spring-boot.version>
<spring-boot-maven-plugin.version>2.7.8</spring-boot-maven-plugin.version>
<micrometer-core.version>1.10.4</micrometer-core.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${micrometer-core.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -135,6 +135,7 @@
<protostuff_version>1.8.0</protostuff_version>
<envoy_api_version>0.1.35</envoy_api_version>
<micrometer.version>1.10.4</micrometer.version>
<micrometer-tracing.version>1.0.2</micrometer-tracing.version>
<t_digest.version>3.3</t_digest.version>
<prometheus_client.version>0.16.0</prometheus_client.version>

View File

@ -487,6 +487,11 @@
<type>pom</type>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-observability-starter</artifactId>
<version>${project.version}</version>
</dependency>
<!-- test -->
<dependency>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-spring-boot-observability-starter</artifactId>
<dependencies>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metrics-default</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.annotation;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@ConditionalOnProperty(prefix = "dubbo.tracing", name = "enabled", matchIfMissing = true)
public @interface ConditionalOnDubboTracingEnable {
}

View File

@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
import io.micrometer.tracing.propagation.Propagator;
import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
/**
* copy from {@link org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration}
* this class is available starting from Boot 3.0. It's not available if you're using Boot < 3.0
*/
@ConditionalOnClass(Tracer.class)
@ConditionalOnDubboTracingEnable
@AutoConfigureAfter(name = "org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration")
public class DubboMicrometerTracingAutoConfiguration {
/**
* {@code @Order} value of
* {@link #propagatingReceiverTracingObservationHandler(Tracer, Propagator)}.
*/
public static final int RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER = 1000;
/**
* {@code @Order} value of
* {@link #propagatingSenderTracingObservationHandler(Tracer, Propagator)}.
*/
public static final int SENDER_TRACING_OBSERVATION_HANDLER_ORDER = 2000;
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Tracer.class)
public DefaultTracingObservationHandler defaultTracingObservationHandler(Tracer tracer) {
return new DefaultTracingObservationHandler(tracer);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean({Tracer.class, Propagator.class})
@Order(SENDER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingSenderTracingObservationHandler<?> propagatingSenderTracingObservationHandler(Tracer tracer,
Propagator propagator) {
return new PropagatingSenderTracingObservationHandler<>(tracer, propagator);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean({Tracer.class, Propagator.class})
@Order(RECEIVER_TRACING_OBSERVATION_HANDLER_ORDER)
public PropagatingReceiverTracingObservationHandler<?> propagatingReceiverTracingObservationHandler(Tracer tracer,
Propagator propagator) {
return new PropagatingReceiverTracingObservationHandler<>(tracer, propagator);
}
}

View File

@ -0,0 +1,153 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
/**
* Register observationRegistry to ApplicationModel.
* Create observationRegistry when you are using Boot <3.0 or you are not using spring-boot-starter-actuator
*/
@AutoConfiguration(after =DubboMicrometerTracingAutoConfiguration.class,afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration")
@ConditionalOnDubboTracingEnable
public class DubboObservationAutoConfiguration implements BeanFactoryAware, SmartInitializingSingleton {
public DubboObservationAutoConfiguration(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
private final ApplicationModel applicationModel;
private BeanFactory beanFactory;
@Bean
@ConditionalOnMissingBean
ObservationRegistry observationRegistry() {
return ObservationRegistry.create();
}
@Bean
@ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
public ObservationRegistryPostProcessor dubboObservationRegistryPostProcessor(ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping,
ObjectProvider<ObservationHandler<?>> observationHandlers) {
return new ObservationRegistryPostProcessor(observationHandlerGrouping, observationHandlers);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void afterSingletonsInstantiated() {
applicationModel.getBeanFactory().registerBean(beanFactory.getBean(ObservationRegistry.class));
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")
@ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
static class OnlyMetricsConfiguration {
@Bean
ObservationHandlerGrouping metricsObservationHandlerGrouping() {
return new ObservationHandlerGrouping(MeterObservationHandler.class);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Tracer.class)
@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
@ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
static class OnlyTracingConfiguration {
@Bean
ObservationHandlerGrouping tracingObservationHandlerGrouping() {
return new ObservationHandlerGrouping(TracingObservationHandler.class);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ MeterRegistry.class, Tracer.class })
@ConditionalOnMissingBean(type = "org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor")
static class MetricsWithTracingConfiguration {
@Bean
ObservationHandlerGrouping metricsAndTracingObservationHandlerGrouping() {
return new ObservationHandlerGrouping(
Arrays.asList(TracingObservationHandler.class, MeterObservationHandler.class));
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MeterRegistry.class)
@ConditionalOnMissingBean(MeterObservationHandler.class)
static class MeterObservationHandlerConfiguration {
@ConditionalOnMissingBean(type = "io.micrometer.tracing.Tracer")
@Configuration(proxyBeanMethods = false)
static class OnlyMetricsMeterObservationHandlerConfiguration {
@Bean
DefaultMeterObservationHandler defaultMeterObservationHandler(MeterRegistry meterRegistry) {
return new DefaultMeterObservationHandler(meterRegistry);
}
}
@ConditionalOnBean(Tracer.class)
@Configuration(proxyBeanMethods = false)
static class TracingAndMetricsObservationHandlerConfiguration {
@Bean
TracingAwareMeterObservationHandler<Observation.Context> tracingAwareMeterObservationHandler(
MeterRegistry meterRegistry, Tracer tracer) {
DefaultMeterObservationHandler delegate = new DefaultMeterObservationHandler(meterRegistry);
return new TracingAwareMeterObservationHandler<>(delegate, tracer);
}
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.Collections;
import java.util.List;
/**
* Groups {@link ObservationHandler ObservationHandlers} by type.
* copy from {@link org.springframework.boot.actuate.autoconfigure.observation.ObservationHandlerGrouping}
* this class is available starting from Boot 3.0. It's not available if you're using Boot < 3.0
*
* @author Andy Wilkinson
*/
class ObservationHandlerGrouping {
private final List<Class<? extends ObservationHandler>> categories;
ObservationHandlerGrouping(Class<? extends ObservationHandler> category) {
this(Collections.singletonList(category));
}
ObservationHandlerGrouping(List<Class<? extends ObservationHandler>> categories) {
this.categories = categories;
}
void apply(List<ObservationHandler<?>> handlers, ObservationRegistry.ObservationConfig config) {
MultiValueMap<Class<? extends ObservationHandler>, ObservationHandler<?>> groupings = new LinkedMultiValueMap<>();
for (ObservationHandler<?> handler : handlers) {
Class<? extends ObservationHandler> category = findCategory(handler);
if (category != null) {
groupings.add(category, handler);
} else {
config.observationHandler(handler);
}
}
for (Class<? extends ObservationHandler> category : this.categories) {
List<ObservationHandler<?>> handlerGroup = groupings.get(category);
if (!CollectionUtils.isEmpty(handlerGroup)) {
config.observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(handlerGroup));
}
}
}
private Class<? extends ObservationHandler> findCategory(ObservationHandler<?> handler) {
for (Class<? extends ObservationHandler> category : this.categories) {
if (category.isInstance(handler)) {
return category;
}
}
return null;
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.util.List;
import java.util.stream.Collectors;
/**
* registry observationHandlers to observationConfig
*/
public class ObservationRegistryPostProcessor implements BeanPostProcessor {
private final ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping;
private final ObjectProvider<ObservationHandler<?>> observationHandlers;
public ObservationRegistryPostProcessor(ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping,
ObjectProvider<ObservationHandler<?>> observationHandlers){
this.observationHandlerGrouping = observationHandlerGrouping;
this.observationHandlers = observationHandlers;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof ObservationRegistry){
ObservationRegistry observationRegistry = (ObservationRegistry) bean;
List<ObservationHandler<?>> observationHandlerList =
observationHandlers.orderedStream().collect(Collectors.toList());
observationHandlerGrouping.ifAvailable(grouping->{
grouping.apply(observationHandlerList,
observationRegistry.observationConfig());
});
}
return bean;
}
}

View File

@ -0,0 +1,261 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure.brave;
import brave.CurrentSpanCustomizer;
import brave.SpanCustomizer;
import brave.Tracing;
import brave.TracingCustomizer;
import brave.baggage.BaggageField;
import brave.baggage.BaggagePropagation;
import brave.baggage.BaggagePropagationConfig;
import brave.baggage.BaggagePropagationCustomizer;
import brave.baggage.CorrelationScopeConfig;
import brave.baggage.CorrelationScopeCustomizer;
import brave.baggage.CorrelationScopeDecorator;
import brave.context.slf4j.MDCScopeDecorator;
import brave.handler.SpanHandler;
import brave.propagation.B3Propagation;
import brave.propagation.CurrentTraceContext;
import brave.propagation.CurrentTraceContextCustomizer;
import brave.propagation.Propagation;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.sampler.Sampler;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.brave.bridge.BraveBaggageManager;
import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext;
import io.micrometer.tracing.brave.bridge.BravePropagator;
import io.micrometer.tracing.brave.bridge.BraveSpanCustomizer;
import io.micrometer.tracing.brave.bridge.BraveTracer;
import io.micrometer.tracing.brave.bridge.CompositeSpanHandler;
import io.micrometer.tracing.brave.bridge.W3CPropagation;
import io.micrometer.tracing.exporter.SpanExportingPredicate;
import io.micrometer.tracing.exporter.SpanFilter;
import io.micrometer.tracing.exporter.SpanReporter;
import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable;
import org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration;
import org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* provider Brave when you are using Boot <3.0 or you are not using spring-boot-starter-actuator
*/
@AutoConfiguration(before = DubboMicrometerTracingAutoConfiguration.class, afterName = "org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration")
@ConditionalOnClass({Tracer.class, BraveTracer.class})
@EnableConfigurationProperties(DubboTracingProperties.class)
@ConditionalOnDubboTracingEnable
public class BraveAutoConfiguration {
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
@Bean
@ConditionalOnMissingBean
@Order(Ordered.HIGHEST_PRECEDENCE)
CompositeSpanHandler compositeSpanHandler(ObjectProvider<SpanExportingPredicate> predicates,
ObjectProvider<SpanReporter> reporters, ObjectProvider<SpanFilter> filters) {
return new CompositeSpanHandler(predicates.orderedStream().collect(Collectors.toList()),
reporters.orderedStream().collect(Collectors.toList()),
filters.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnMissingBean
public Tracing braveTracing(Environment environment, List<SpanHandler> spanHandlers,
List<TracingCustomizer> tracingCustomizers, CurrentTraceContext currentTraceContext,
Propagation.Factory propagationFactory, Sampler sampler) {
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
Tracing.Builder builder = Tracing.newBuilder().currentTraceContext(currentTraceContext).traceId128Bit(true)
.supportsJoin(false).propagationFactory(propagationFactory).sampler(sampler)
.localServiceName(applicationName);
spanHandlers.forEach(builder::addSpanHandler);
for (TracingCustomizer tracingCustomizer : tracingCustomizers) {
tracingCustomizer.customize(builder);
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
public brave.Tracer braveTracer(Tracing tracing) {
return tracing.tracer();
}
@Bean
@ConditionalOnMissingBean
public CurrentTraceContext braveCurrentTraceContext(List<CurrentTraceContext.ScopeDecorator> scopeDecorators,
List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) {
ThreadLocalCurrentTraceContext.Builder builder = ThreadLocalCurrentTraceContext.newBuilder();
scopeDecorators.forEach(builder::addScopeDecorator);
for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
currentTraceContextCustomizer.customize(builder);
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
public Sampler braveSampler(DubboTracingProperties properties) {
return Sampler.create(properties.getSampling().getProbability());
}
@Bean
@ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class)
BraveTracer braveTracerBridge(brave.Tracer tracer, CurrentTraceContext currentTraceContext) {
return new BraveTracer(tracer, new BraveCurrentTraceContext(currentTraceContext), BRAVE_BAGGAGE_MANAGER);
}
@Bean
@ConditionalOnMissingBean
BravePropagator bravePropagator(Tracing tracing) {
return new BravePropagator(tracing);
}
@Bean
@ConditionalOnMissingBean(SpanCustomizer.class)
CurrentSpanCustomizer currentSpanCustomizer(Tracing tracing) {
return CurrentSpanCustomizer.create(tracing);
}
@Bean
@ConditionalOnMissingBean(io.micrometer.tracing.SpanCustomizer.class)
BraveSpanCustomizer braveSpanCustomizer(SpanCustomizer spanCustomizer) {
return new BraveSpanCustomizer(spanCustomizer);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "dubbo.tracing.baggage.enabled", havingValue = "false")
static class BraveNoBaggageConfiguration {
@Bean
@ConditionalOnMissingBean
Propagation.Factory propagationFactory(DubboTracingProperties tracing) {
DubboTracingProperties.Propagation.PropagationType type = tracing.getPropagation().getType();
switch (type) {
case B3:
return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
case W3C:
return new W3CPropagation();
default:
throw new IllegalArgumentException("UnSupport propagation type");
}
}
}
@ConditionalOnProperty(value = "dubbo.tracing.baggage.enabled", matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
static class BraveBaggageConfiguration {
private final DubboTracingProperties dubboTracingProperties;
public BraveBaggageConfiguration(DubboTracingProperties dubboTracingProperties) {
this.dubboTracingProperties = dubboTracingProperties;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", value = "type", havingValue = "B3")
BaggagePropagation.FactoryBuilder b3PropagationFactoryBuilder(
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
Propagation.Factory delegate =
B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
BaggagePropagation.FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate);
baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", value = "type", havingValue = "W3C", matchIfMissing = true)
BaggagePropagation.FactoryBuilder w3cPropagationFactoryBuilder(
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
Propagation.Factory delegate = new W3CPropagation(BRAVE_BAGGAGE_MANAGER, Collections.emptyList());
BaggagePropagation.FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(delegate);
baggagePropagationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
@Bean
@ConditionalOnMissingBean
@Order(0)
BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() {
return (builder) -> {
List<String> remoteFields = dubboTracingProperties.getBaggage().getRemoteFields();
for (String fieldName : remoteFields) {
builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName)));
}
};
}
@Bean
@ConditionalOnMissingBean
Propagation.Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) {
return factoryBuilder.build();
}
@Bean
@ConditionalOnMissingBean
CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder(
ObjectProvider<CorrelationScopeCustomizer> correlationScopeCustomizers) {
CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder();
correlationScopeCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
@Bean
@Order(0)
@ConditionalOnProperty(prefix = "dubbo.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() {
return (builder) -> {
List<String> correlationFields = this.dubboTracingProperties.getBaggage().getCorrelation().getFields();
for (String field : correlationFields) {
builder.add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(BaggageField.create(field))
.flushOnUpdate().build());
}
};
}
@Bean
@ConditionalOnMissingBean(CurrentTraceContext.ScopeDecorator.class)
CurrentTraceContext.ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) {
return builder.build();
}
}
}

View File

@ -0,0 +1,257 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.autoconfigure.otel;
import io.micrometer.tracing.SpanCustomizer;
import io.micrometer.tracing.exporter.SpanExportingPredicate;
import io.micrometer.tracing.exporter.SpanFilter;
import io.micrometer.tracing.exporter.SpanReporter;
import io.micrometer.tracing.otel.bridge.CompositeSpanExporter;
import io.micrometer.tracing.otel.bridge.EventListener;
import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelPropagator;
import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.spring.boot.observability.annotation.ConditionalOnDubboTracingEnable;
import org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration;
import org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* provider OpenTelemetry when you are using Boot <3.0 or you are not using spring-boot-starter-actuator
*/
@AutoConfiguration(before = DubboMicrometerTracingAutoConfiguration.class, afterName = "org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration")
@ConditionalOnDubboTracingEnable
@ConditionalOnClass({OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class})
@EnableConfigurationProperties(DubboTracingProperties.class)
public class OpenTelemetryAutoConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
private final DubboTracingProperties dubboTracingProperties;
OpenTelemetryAutoConfiguration(DubboTracingProperties dubboTracingProperties) {
this.dubboTracingProperties = dubboTracingProperties;
}
@Bean
@ConditionalOnMissingBean
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setPropagators(contextPropagators)
.build();
}
@Bean
@ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider<SpanProcessor> spanProcessors,
Sampler sampler) {
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(builder::addSpanProcessor);
return builder.build();
}
@Bean
@ConditionalOnMissingBean
ContextPropagators otelContextPropagators(ObjectProvider<TextMapPropagator> textMapPropagators) {
return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators.orderedStream().collect(Collectors.toList())));
}
@Bean
@ConditionalOnMissingBean
Sampler otelSampler() {
Sampler rootSampler = Sampler.traceIdRatioBased(this.dubboTracingProperties.getSampling().getProbability());
return Sampler.parentBased(rootSampler);
}
@Bean
@ConditionalOnMissingBean
SpanProcessor otelSpanProcessor(ObjectProvider<SpanExporter> spanExporters,
ObjectProvider<SpanExportingPredicate> spanExportingPredicates, ObjectProvider<SpanReporter> spanReporters,
ObjectProvider<SpanFilter> spanFilters) {
return BatchSpanProcessor.builder(new CompositeSpanExporter(spanExporters.orderedStream().collect(Collectors.toList()),
spanExportingPredicates.orderedStream().collect(Collectors.toList()), spanReporters.orderedStream().collect(Collectors.toList()),
spanFilters.orderedStream().collect(Collectors.toList()))).build();
}
@Bean
@ConditionalOnMissingBean
Tracer otelTracer(OpenTelemetry openTelemetry) {
return openTelemetry.getTracer("org.apache.dubbo", Version.getVersion());
}
@Bean
@ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class)
OtelTracer micrometerOtelTracer(Tracer tracer, OtelTracer.EventPublisher eventPublisher,
OtelCurrentTraceContext otelCurrentTraceContext) {
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher,
new OtelBaggageManager(otelCurrentTraceContext, this.dubboTracingProperties.getBaggage().getRemoteFields(),
Collections.emptyList()));
}
@Bean
@ConditionalOnMissingBean
OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) {
return new OtelPropagator(contextPropagators, tracer);
}
@Bean
@ConditionalOnMissingBean
OtelTracer.EventPublisher otelTracerEventPublisher(List<EventListener> eventListeners) {
return new OTelEventPublisher(eventListeners);
}
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext(OtelTracer.EventPublisher publisher) {
ContextStorage.addWrapper(new EventPublishingContextWrapper(publisher));
return new OtelCurrentTraceContext();
}
@Bean
@ConditionalOnMissingBean
Slf4JEventListener otelSlf4JEventListener() {
return new Slf4JEventListener();
}
@Bean
@ConditionalOnMissingBean(SpanCustomizer.class)
OtelSpanCustomizer otelSpanCustomizer() {
return new OtelSpanCustomizer();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "dubbo.tracing.baggage", name = "enabled", matchIfMissing = true)
static class BaggageConfiguration {
private final DubboTracingProperties tracingProperties;
BaggageConfiguration(DubboTracingProperties tracingProperties) {
this.tracingProperties = tracingProperties;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "W3C",
matchIfMissing = true)
TextMapPropagator w3cTextMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
return TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
W3CBaggagePropagator.getInstance(), new BaggageTextMapPropagator(remoteFields,
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList())));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "B3")
TextMapPropagator b3BaggageTextMapPropagator(OtelCurrentTraceContext otelCurrentTraceContext) {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
return TextMapPropagator.composite(B3Propagator.injectingSingleHeader(),
new BaggageTextMapPropagator(remoteFields,
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList())));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
Slf4JBaggageEventListener otelSlf4JBaggageEventListener() {
return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields());
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "dubbo.tracing.baggage", name = "enabled", havingValue = "false")
static class NoBaggageConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "B3")
B3Propagator b3TextMapPropagator() {
return B3Propagator.injectingSingleHeader();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "dubbo.tracing.propagation", name = "type", havingValue = "W3C",
matchIfMissing = true)
W3CTraceContextPropagator w3cTextMapPropagatorWithoutBaggage() {
return W3CTraceContextPropagator.getInstance();
}
}
static class OTelEventPublisher implements OtelTracer.EventPublisher {
private final List<EventListener> listeners;
OTelEventPublisher(List<EventListener> listeners) {
this.listeners = listeners;
}
@Override
public void publishEvent(Object event) {
for (EventListener listener : this.listeners) {
listener.onEvent(event);
}
}
}
}

View File

@ -0,0 +1,191 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.boot.observability.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
@ConfigurationProperties("dubbo.tracing")
public class DubboTracingProperties {
/**
* Whether auto-configuration of tracing is enabled.
*/
private boolean enabled = true;
/**
* Sampling configuration.
*/
private final Sampling sampling = new Sampling();
/**
* Baggage configuration.
*/
private final Baggage baggage = new Baggage();
/**
* Propagation configuration.
*/
private final Propagation propagation = new Propagation();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Sampling getSampling() {
return this.sampling;
}
public Baggage getBaggage() {
return this.baggage;
}
public Propagation getPropagation() {
return this.propagation;
}
public static class Sampling {
/**
* Probability in the range from 0.0 to 1.0 that a trace will be sampled.
*/
private float probability = 0.10f;
public float getProbability() {
return this.probability;
}
public void setProbability(float probability) {
this.probability = probability;
}
}
public static class Baggage {
/**
* Whether to enable Micrometer Tracing baggage propagation.
*/
private boolean enabled = true;
/**
* Correlation configuration.
*/
private Correlation correlation = new Correlation();
/**
* List of fields that are referenced the same in-process as it is on the wire.
* For example, the field "x-vcap-request-id" would be set as-is including the
* prefix.
*/
private List<String> remoteFields = new ArrayList<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Correlation getCorrelation() {
return this.correlation;
}
public void setCorrelation(Correlation correlation) {
this.correlation = correlation;
}
public List<String> getRemoteFields() {
return this.remoteFields;
}
public void setRemoteFields(List<String> remoteFields) {
this.remoteFields = remoteFields;
}
public static class Correlation {
/**
* Whether to enable correlation of the baggage context with logging contexts.
*/
private boolean enabled = true;
/**
* List of fields that should be correlated with the logging context. That
* means that these fields would end up as key-value pairs in e.g. MDC.
*/
private List<String> fields = new ArrayList<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getFields() {
return this.fields;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
}
}
public static class Propagation {
/**
* Tracing context propagation type.
*/
private PropagationType type = PropagationType.W3C;
public PropagationType getType() {
return this.type;
}
public void setType(PropagationType type) {
this.type = type;
}
public enum PropagationType {
/**
* B3 propagation type.
*/
B3,
/**
* W3C propagation type.
*/
W3C
}
}
}

View File

@ -0,0 +1,82 @@
{
"groups": [
{
"name": "dubbo.tracing",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties"
},
{
"name": "dubbo.tracing.baggage",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties",
"sourceMethod": "getBaggage()"
},
{
"name": "dubbo.tracing.baggage.correlation",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage",
"sourceMethod": "getCorrelation()"
},
{
"name": "dubbo.tracing.propagation",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties",
"sourceMethod": "getPropagation()"
},
{
"name": "dubbo.tracing.sampling",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Sampling",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties",
"sourceMethod": "getSampling()"
}
],
"properties": [
{
"name": "dubbo.tracing.baggage.correlation.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable correlation of the baggage context with logging contexts.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation",
"defaultValue": true
},
{
"name": "dubbo.tracing.baggage.correlation.fields",
"type": "java.util.List<java.lang.String>",
"description": "List of fields that should be correlated with the logging context. That means that these fields would end up as key-value pairs in e.g. MDC.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage$Correlation"
},
{
"name": "dubbo.tracing.baggage.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable Micrometer Tracing baggage propagation.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage",
"defaultValue": true
},
{
"name": "dubbo.tracing.baggage.remote-fields",
"type": "java.util.List<java.lang.String>",
"description": "List of fields that are referenced the same in-process as it is on the wire. For example, the field \"x-vcap-request-id\" would be set as-is including the prefix.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Baggage"
},
{
"name": "dubbo.tracing.enabled",
"type": "java.lang.Boolean",
"description": "Whether auto-configuration of tracing is enabled.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties",
"defaultValue": true
},
{
"name": "dubbo.tracing.propagation.type",
"type": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation$PropagationType",
"description": "Tracing context propagation type.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Propagation"
},
{
"name": "dubbo.tracing.sampling.probability",
"type": "java.lang.Float",
"description": "Probability in the range from 0.0 to 1.0 that a trace will be sampled.",
"sourceType": "org.apache.dubbo.spring.boot.observability.config.DubboTracingProperties$Sampling",
"defaultValue": 0.1
}
],
"hints": []
}

View File

@ -0,0 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.observability.autoconfigure.otel.OpenTelemetryAutoConfiguration,\
org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration,\
org.apache.dubbo.spring.boot.observability.autoconfigure.DubboObservationAutoConfiguration,\
org.apache.dubbo.spring.boot.observability.autoconfigure.brave.BraveAutoConfiguration

View File

@ -0,0 +1,4 @@
org.apache.dubbo.spring.boot.observability.autoconfigure.otel.OpenTelemetryAutoConfiguration
org.apache.dubbo.spring.boot.observability.autoconfigure.DubboMicrometerTracingAutoConfiguration
org.apache.dubbo.spring.boot.observability.autoconfigure.DubboObservationAutoConfiguration
org.apache.dubbo.spring.boot.observability.autoconfigure.brave.BraveAutoConfiguration

View File

@ -36,6 +36,7 @@
<module>dubbo-spring-boot-autoconfigure</module>
<module>dubbo-spring-boot-compatible</module>
<module>dubbo-spring-boot-starter</module>
<module>dubbo-spring-boot-observability-starter</module>
</modules>
<properties>
@ -45,6 +46,7 @@
<log4j2_version>2.19.0</log4j2_version>
<!-- Spring boot buddy is lower than the delivery dependency package version and can only show the defined dependency version -->
<byte-buddy.version>1.13.0</byte-buddy.version>
<micrometer-core.version>1.10.4</micrometer-core.version>
</properties>
<dependencyManagement>
@ -57,6 +59,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${micrometer-core.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -318,6 +318,11 @@
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-observability-starter</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>org.apache.dubbo</groupId>