Support Observations for CommandLineRunner and ApplicationRunner
First pass on observation Added automatic docs generation Renamed Observed -> Observation Fixed copyright dates Removed execution task listener - it makes runner spans share the same trace id Updated version for micrometer docs to milestone
This commit is contained in:
committed by
Glenn Renfro
parent
79a9a59e08
commit
3817fe14e7
58
docs/pom.xml
58
docs/pom.xml
@@ -15,6 +15,12 @@
|
||||
<configprops.inclusionPattern>spring.cloud.task.*</configprops.inclusionPattern>
|
||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||
<asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version>
|
||||
|
||||
<!-- Observability -->
|
||||
<micrometer-docs-generator.version>1.0.0-M4</micrometer-docs-generator.version>
|
||||
<micrometer-docs-generator.inputPath>${maven.multiModuleProjectDirectory}/spring-cloud-task-core/</micrometer-docs-generator.inputPath>
|
||||
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
|
||||
<micrometer-docs-generator.outputPath>${maven.multiModuleProjectDirectory}/target/</micrometer-docs-generator.outputPath>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -49,6 +55,58 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-metrics-metadata</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>io.micrometer.docs.metrics.DocsFromSources</mainClass>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
<arguments>
|
||||
<argument>${micrometer-docs-generator.inputPath}</argument>
|
||||
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
|
||||
<argument>${micrometer-docs-generator.outputPath}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generate-tracing-metadata</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>io.micrometer.docs.spans.DocsFromSources</mainClass>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
<arguments>
|
||||
<argument>${micrometer-docs-generator.inputPath}</argument>
|
||||
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
|
||||
<argument>${micrometer-docs-generator.outputPath}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.micrometer
|
||||
</groupId>
|
||||
<artifactId>micrometer-docs-generator-spans</artifactId>
|
||||
<version>${micrometer-docs-generator.version}
|
||||
</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer
|
||||
</groupId>
|
||||
<artifactId>micrometer-docs-generator-metrics</artifactId>
|
||||
<version>${micrometer-docs-generator.version}
|
||||
</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
|
||||
8
docs/src/main/asciidoc/_observability.adoc
Normal file
8
docs/src/main/asciidoc/_observability.adoc
Normal file
@@ -0,0 +1,8 @@
|
||||
:root-target: ../../../../target/
|
||||
|
||||
[[observability]]
|
||||
== Observability metadata
|
||||
|
||||
include::{root-target}_metrics.adoc[]
|
||||
|
||||
include::{root-target}_spans.adoc[]
|
||||
@@ -3,4 +3,7 @@
|
||||
= Appendices
|
||||
|
||||
include::appendix-task-repository-schema.adoc[]
|
||||
|
||||
include::appendix-building-the-documentation.adoc[]
|
||||
|
||||
include::_observability.adoc[]
|
||||
|
||||
@@ -109,10 +109,19 @@
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
@@ -129,6 +138,41 @@
|
||||
<version>${jmock-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-bridge-brave</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-tests</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter-brave</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-urlconnection</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
/**
|
||||
* {@link Observation.KeyValuesProvider} for Spring Cloud Task.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class DefaultTaskKeyValuesProvider implements TaskKeyValuesProvider {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(TaskObservationContext context) {
|
||||
return KeyValues.of(TaskDocumentedObservation.TaskRunnerTags.BEAN_NAME.of(context.getBeanName()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
|
||||
/**
|
||||
* Observed representation of a {@link ApplicationRunner}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class ObservationApplicationRunner implements ApplicationRunner, Observation.KeyValuesProviderAware<TaskKeyValuesProvider> {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final ApplicationRunner delegate;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private TaskKeyValuesProvider keyValuesProvider = new DefaultTaskKeyValuesProvider();
|
||||
|
||||
ObservationApplicationRunner(BeanFactory beanFactory, ApplicationRunner delegate, String beanName) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.delegate = delegate;
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
TaskObservationContext context = new TaskObservationContext(this.beanName);
|
||||
Observation observation = TaskDocumentedObservation.TASK_RUNNER_OBSERVATION.observation(registry(), context)
|
||||
.contextualName(this.beanName)
|
||||
.keyValuesProvider(this.keyValuesProvider);
|
||||
try (Observation.Scope scope = observation.start().openScope()) {
|
||||
this.delegate.run(args);
|
||||
}
|
||||
catch (Exception error) {
|
||||
observation.error(error);
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
observation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private ObservationRegistry registry() {
|
||||
if (this.registry == null) {
|
||||
this.registry = this.beanFactory.getBean(ObservationRegistry.class);
|
||||
}
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyValuesProvider(TaskKeyValuesProvider keyValuesProvider) {
|
||||
this.keyValuesProvider = keyValuesProvider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
|
||||
/**
|
||||
* Registers beans related to task scheduling.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class ObservationApplicationRunnerBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
ObservationApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ApplicationRunner applicationRunner && !(bean instanceof ObservationApplicationRunner)) {
|
||||
return new ObservationApplicationRunner(this.beanFactory, applicationRunner, beanName);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
|
||||
/**
|
||||
* Observed representation of a {@link CommandLineRunner}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class ObservationCommandLineRunner implements CommandLineRunner, Observation.KeyValuesProviderAware<TaskKeyValuesProvider> {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final CommandLineRunner delegate;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private ObservationRegistry registry;
|
||||
|
||||
private TaskKeyValuesProvider keyValuesProvider = new DefaultTaskKeyValuesProvider();
|
||||
|
||||
ObservationCommandLineRunner(BeanFactory beanFactory, CommandLineRunner delegate, String beanName) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.delegate = delegate;
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
TaskObservationContext context = new TaskObservationContext(this.beanName);
|
||||
Observation observation = TaskDocumentedObservation.TASK_RUNNER_OBSERVATION.observation(registry(), context)
|
||||
.contextualName(this.beanName)
|
||||
.keyValuesProvider(this.keyValuesProvider);
|
||||
try (Observation.Scope scope = observation.start().openScope()) {
|
||||
this.delegate.run(args);
|
||||
}
|
||||
catch (Exception error) {
|
||||
observation.error(error);
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
observation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private ObservationRegistry registry() {
|
||||
if (this.registry == null) {
|
||||
this.registry = this.beanFactory.getBean(ObservationRegistry.class);
|
||||
}
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyValuesProvider(TaskKeyValuesProvider keyValuesProvider) {
|
||||
this.keyValuesProvider = keyValuesProvider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
|
||||
/**
|
||||
* Registers beans related to task scheduling.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class ObservationCommandLineRunnerBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
ObservationCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof CommandLineRunner commandLineRunner && !(bean instanceof ObservationCommandLineRunner)) {
|
||||
return new ObservationCommandLineRunner(this.beanFactory, commandLineRunner, beanName);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
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.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} that registers instrumentation for Spring Cloud Task.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ObservationRegistry.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.task.observation.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(ObservationRegistry.class)
|
||||
@AutoConfigureAfter(ObservationAutoConfiguration.class)
|
||||
public class ObservationTaskAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ObservationCommandLineRunnerBeanPostProcessor observedCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new ObservationCommandLineRunnerBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
static ObservationApplicationRunnerBeanPostProcessor observedApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new ObservationApplicationRunnerBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.common.docs.KeyName;
|
||||
import io.micrometer.observation.docs.DocumentedObservation;
|
||||
|
||||
enum TaskDocumentedObservation implements DocumentedObservation {
|
||||
|
||||
/**
|
||||
* Observation created when a task runner is executed.
|
||||
*/
|
||||
TASK_RUNNER_OBSERVATION {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "spring.cloud.task.runner";
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyName[] getLowCardinalityKeyNames() {
|
||||
return TaskRunnerTags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return "spring.cloud.task";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Key names for Spring Cloud Task Command / Application runners.
|
||||
*/
|
||||
enum TaskRunnerTags implements KeyName {
|
||||
|
||||
/**
|
||||
* Name of the bean that was executed by Spring Cloud Task.
|
||||
*/
|
||||
BEAN_NAME {
|
||||
@Override
|
||||
public String getKeyName() {
|
||||
return "spring.cloud.task.runner.bean-name";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
/**
|
||||
* {@link Observation.KeyValuesProvider} for Spring Cloud Task.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public interface TaskKeyValuesProvider extends Observation.KeyValuesProvider<TaskObservationContext> {
|
||||
|
||||
@Override
|
||||
default boolean supportsContext(Observation.Context context) {
|
||||
return context instanceof TaskObservationContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
/**
|
||||
* {@link Observation.Context} for Spring Cloud Task.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class TaskObservationContext extends Observation.Context {
|
||||
|
||||
private final String beanName;
|
||||
|
||||
public TaskObservationContext(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.task.configuration.SingleTaskConfiguration,\
|
||||
org.springframework.cloud.task.configuration.SimpleTaskAutoConfiguration,\
|
||||
org.springframework.cloud.task.configuration.MetricsAutoConfiguration\
|
||||
org.springframework.cloud.task.configuration.MetricsAutoConfiguration,\
|
||||
org.springframework.cloud.task.configuration.observation.ObservationTaskAutoConfiguration
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.cloud.task.configuration.observation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import brave.handler.SpanHandler;
|
||||
import brave.sampler.Sampler;
|
||||
import brave.test.TestSpanHandler;
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.tck.MeterRegistryAssert;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.brave.bridge.BraveFinishedSpan;
|
||||
import io.micrometer.tracing.exporter.FinishedSpan;
|
||||
import io.micrometer.tracing.test.simple.SpansAssert;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import zipkin2.Span;
|
||||
import zipkin2.reporter.Reporter;
|
||||
import zipkin2.reporter.brave.ZipkinSpanHandler;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.task.configuration.EnableTask;
|
||||
import org.springframework.cloud.task.configuration.SimpleTaskAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@SpringBootTest(classes = ObservationIntegrationTests.Config.class)
|
||||
class ObservationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
TestSpanHandler testSpanHandler;
|
||||
|
||||
@Autowired
|
||||
MeterRegistry meterRegistry;
|
||||
|
||||
@Test
|
||||
void testSuccessfulObservation() {
|
||||
List<FinishedSpan> finishedSpans = finishedSpans();
|
||||
|
||||
SpansAssert.then(finishedSpans)
|
||||
.thenASpanWithNameEqualTo("my-command-line-runner")
|
||||
.hasTag("spring.cloud.task.runner.bean-name", "myCommandLineRunner")
|
||||
.backToSpans()
|
||||
.thenASpanWithNameEqualTo("my-application-runner")
|
||||
.hasTag("spring.cloud.task.runner.bean-name", "myApplicationRunner");
|
||||
MeterRegistryAssert.then(this.meterRegistry)
|
||||
.hasTimerWithNameAndTags("spring.cloud.task.runner", KeyValues.of("spring.cloud.task.runner.bean-name", "myCommandLineRunner"))
|
||||
.hasTimerWithNameAndTags("spring.cloud.task.runner", KeyValues.of("spring.cloud.task.runner.bean-name", "myApplicationRunner"));
|
||||
}
|
||||
|
||||
private List<FinishedSpan> finishedSpans() {
|
||||
return this.testSpanHandler.spans().stream().map(BraveFinishedSpan::fromBrave).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportAutoConfiguration({SimpleTaskAutoConfiguration.class, ObservationAutoConfiguration.class, ObservationTaskAutoConfiguration.class, BraveAutoConfiguration.class, MicrometerTracingAutoConfiguration.class, MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class, ZipkinAutoConfiguration.class})
|
||||
@EnableTask
|
||||
static class Config {
|
||||
private static final Logger log = LoggerFactory.getLogger(Config.class);
|
||||
|
||||
@Bean
|
||||
TestSpanHandler testSpanHandler() {
|
||||
return new TestSpanHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanHandler zipkinSpanHandler(Reporter<Span> spanReporter) {
|
||||
return ZipkinSpanHandler.newBuilder(spanReporter).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner myCommandLineRunner(Tracer tracer) {
|
||||
return args -> log.info("<TRACE:{}> Hello from command line runner", tracer.currentSpan().context().traceId());
|
||||
}
|
||||
|
||||
@Bean
|
||||
ApplicationRunner myApplicationRunner(Tracer tracer) {
|
||||
return args -> log.info("<TRACE:{}> Hello from application runner", tracer.currentSpan().context().traceId());
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/checkstyle/checkstyle-suppressions.xml
Normal file
7
src/checkstyle/checkstyle-suppressions.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files=".*ObservationTaskAutoConfiguration.*" checks="HideUtilityClassConstructorCheck"/>
|
||||
</suppressions>
|
||||
Reference in New Issue
Block a user