diff --git a/docs/src/main/asciidoc/features.adoc b/docs/src/main/asciidoc/features.adoc
index 0d0d4936..3250cd2a 100644
--- a/docs/src/main/asciidoc/features.adoc
+++ b/docs/src/main/asciidoc/features.adoc
@@ -448,3 +448,21 @@ In these cases the context is held open because a thread has been allocated
set the `spring.cloud.task.closecontextEnabled` property to `true` when launching your task.
This will close the application's context once the task is complete.
Thus allowing the application to terminate.
+
+[[enable-task-metrics]]
+=== Enable Task Metrics
+Spring Cloud Task integrates with Micrometer and creates observations for the Tasks it executes.
+To enable Task Observability integration, you must add `spring-boot-starter-actuator`, your preferred registry implementation (if you want to publish metrics), and micrometer-tracing (if you want to publish tracing data) to your task application.
+An example maven set of dependencies to enable task observability and metrics using Influx would be:
+
+[source,xml]
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.micrometer
+ micrometer-registry-influx
+ runtime
+
+
diff --git a/spring-cloud-task-core/pom.xml b/spring-cloud-task-core/pom.xml
index 4d77103d..2ef20c63 100755
--- a/spring-cloud-task-core/pom.xml
+++ b/spring-cloud-task-core/pom.xml
@@ -13,9 +13,6 @@
jar
Spring Cloud Task Core
Spring Cloud Task
-
- 1.49
-
@@ -123,19 +120,8 @@
micrometer-observation
- org.junit.jupiter
- junit-jupiter-params
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.jmockit
- jmockit
- ${jmock-version}
+ io.micrometer
+ micrometer-test
test
@@ -144,8 +130,13 @@
test
- io.micrometer
- micrometer-test
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/MetricsAutoConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/MetricsAutoConfiguration.java
deleted file mode 100644
index 2b4553f7..00000000
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/MetricsAutoConfiguration.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2019-2019 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;
-
-import java.util.Arrays;
-
-import io.micrometer.core.annotation.Timed;
-import io.micrometer.core.instrument.Clock;
-import io.micrometer.core.instrument.MeterRegistry;
-
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.beans.factory.config.BeanPostProcessor;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.Environment;
-
-/**
- * Forked and simplified version of the
- * org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration form
- * spring-boot-actuator
- *
- * {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics.
- *
- * @author Michael Minella
- * @since 2.2
- */
-@Configuration(proxyBeanMethods = false)
-@ConditionalOnClass(Timed.class)
-@AutoConfigureBefore(name = {
- "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration" })
-public class MetricsAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public Clock micrometerClockFork() {
- return Clock.SYSTEM;
- }
-
- @Bean
- public static MeterRegistryPostProcessor meterRegistryPostProcessorFork() {
- return new MeterRegistryPostProcessor();
- }
-
- static class MeterRegistryPostProcessor
- implements BeanPostProcessor, EnvironmentAware {
-
- private Environment environment;
-
- @Value("${spring.cloud.task.name:unknown}")
- private String taskName;
-
- @Value("${spring.cloud.task.executionid:unknown}")
- private String taskExecutionId;
-
- @Value("${spring.cloud.task.external-execution-id:unknown}")
- private String taskExternalExecutionId;
-
- @Value("${spring.cloud.task.parent-execution-id:unknown}")
- private String taskParentExecutionId;
-
- @Value("${vcap.application.org_name:default}")
- private String organizationName;
-
- @Value("${vcap.application.space_id:unknown}")
- private String spaceId;
-
- @Value("${vcap.application.space_name:unknown}")
- private String spaceName;
-
- @Value("${vcap.application.application_name:unknown}")
- private String applicationName;
-
- @Value("${vcap.application.application_id:unknown}")
- private String applicationId;
-
- @Value("${vcap.application.application_version:unknown}")
- private String applicationVersion;
-
- @Value("${vcap.application.instance_index:0}")
- private String instanceIndex;
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName)
- throws BeansException {
-
- if (bean instanceof MeterRegistry) {
- MeterRegistry registry = (MeterRegistry) bean;
-
- if (Arrays.asList(this.environment.getActiveProfiles())
- .contains("cloud")) {
- registry.config().commonTags("cf.org.name", organizationName)
- .commonTags("cf.space.id", spaceId)
- .commonTags("cf.space.name", spaceName)
- .commonTags("cf.app.id", applicationId)
- .commonTags("cf.app.name", applicationName)
- .commonTags("cf.app.version", applicationVersion)
- .commonTags("cf.instance.index", instanceIndex);
- }
-
- registry.config().commonTags("task.name", taskName)
- .commonTags("task.execution.id", taskExecutionId)
- .commonTags("task.external.execution.id", taskExternalExecutionId)
- .commonTags("task.parent.execution.id", taskParentExecutionId);
- }
-
- return bean;
- }
-
- @Override
- public void setEnvironment(Environment environment) {
- this.environment = environment;
- }
-
- }
-
-}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java
index c53a8c94..0a56db02 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java
@@ -38,6 +38,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Profile;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.CollectionUtils;
@@ -113,6 +114,13 @@ public class SimpleTaskAutoConfiguration {
return taskRepositoryInitializer;
}
+
+ @Bean
+ @Profile("cloud")
+ TaskObservationCloudKeyValues taskObservationCloudKeyValues() {
+ return new TaskObservationCloudKeyValues();
+ }
+
/**
* Determines the {@link TaskConfigurer} to use.
*/
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskLifecycleConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskLifecycleConfiguration.java
index 4f823acd..c0c0d134 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskLifecycleConfiguration.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskLifecycleConfiguration.java
@@ -16,6 +16,7 @@
package org.springframework.cloud.task.configuration;
+import io.micrometer.observation.ObservationRegistry;
import jakarta.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -61,17 +62,27 @@ public class TaskLifecycleConfiguration {
private boolean initialized = false;
+ private ObservationRegistry observationRegistry;
+
+ private TaskObservationCloudKeyValues taskObservationCloudKeyValues;
+
@Autowired
public TaskLifecycleConfiguration(TaskProperties taskProperties,
- ConfigurableApplicationContext context, TaskRepository taskRepository,
- TaskExplorer taskExplorer, TaskNameResolver taskNameResolver,
- ObjectProvider applicationArguments) {
+ ConfigurableApplicationContext context, TaskRepository taskRepository,
+ TaskExplorer taskExplorer, TaskNameResolver taskNameResolver,
+ ObjectProvider applicationArguments,
+ @Autowired(required = false) ObservationRegistry observationRegistry,
+ @Autowired(required = false) TaskObservationCloudKeyValues taskObservationCloudKeyValues) {
+
this.taskProperties = taskProperties;
this.context = context;
this.taskRepository = taskRepository;
this.taskExplorer = taskExplorer;
this.taskNameResolver = taskNameResolver;
this.applicationArguments = applicationArguments.getIfAvailable();
+ this.observationRegistry = observationRegistry == null ? ObservationRegistry.NOOP : observationRegistry;
+ this.taskObservationCloudKeyValues = taskObservationCloudKeyValues;
+
}
@Bean
@@ -88,7 +99,8 @@ public class TaskLifecycleConfiguration {
this.taskLifecycleListener = new TaskLifecycleListener(this.taskRepository,
this.taskNameResolver, this.applicationArguments, this.taskExplorer,
this.taskProperties,
- new TaskListenerExecutorObjectFactory(this.context));
+ new TaskListenerExecutorObjectFactory(this.context),
+ this.observationRegistry, taskObservationCloudKeyValues);
this.initialized = true;
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskObservationCloudKeyValues.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskObservationCloudKeyValues.java
new file mode 100644
index 00000000..335c1a33
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskObservationCloudKeyValues.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2022-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;
+
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * Provides values for the {@link io.micrometer.common.KeyValues} for the task
+ * {@link io.micrometer.observation.Observation} when the cloud profile is active.
+ *
+ * @author Glenn Renfro
+ * @since 3.0
+ */
+public class TaskObservationCloudKeyValues {
+
+ @Value("${vcap.application.org_name:default}")
+ private String organizationName;
+
+ @Value("${vcap.application.space_id:unknown}")
+ private String spaceId;
+
+ @Value("${vcap.application.space_name:unknown}")
+ private String spaceName;
+
+ @Value("${vcap.application.application_name:unknown}")
+ private String applicationName;
+
+ @Value("${vcap.application.application_id:unknown}")
+ private String applicationId;
+
+ @Value("${vcap.application.application_version:unknown}")
+ private String applicationVersion;
+
+ @Value("${vcap.application.instance_index:0}")
+ private String instanceIndex;
+
+ public String getOrganizationName() {
+ return organizationName;
+ }
+
+ public void setOrganizationName(String organizationName) {
+ this.organizationName = organizationName;
+ }
+
+ public String getSpaceId() {
+ return spaceId;
+ }
+
+ public void setSpaceId(String spaceId) {
+ this.spaceId = spaceId;
+ }
+
+ public String getSpaceName() {
+ return spaceName;
+ }
+
+ public void setSpaceName(String spaceName) {
+ this.spaceName = spaceName;
+ }
+
+ public String getApplicationName() {
+ return applicationName;
+ }
+
+ public void setApplicationName(String applicationName) {
+ this.applicationName = applicationName;
+ }
+
+ public String getApplicationId() {
+ return applicationId;
+ }
+
+ public void setApplicationId(String applicationId) {
+ this.applicationId = applicationId;
+ }
+
+ public String getApplicationVersion() {
+ return applicationVersion;
+ }
+
+ public void setApplicationVersion(String applicationVersion) {
+ this.applicationVersion = applicationVersion;
+ }
+
+ public String getInstanceIndex() {
+ return instanceIndex;
+ }
+
+ public void setInstanceIndex(String instanceIndex) {
+ this.instanceIndex = instanceIndex;
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/DefaultTaskObservationConvention.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/DefaultTaskObservationConvention.java
index 65f8250b..52c9b75c 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/DefaultTaskObservationConvention.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/DefaultTaskObservationConvention.java
@@ -20,7 +20,7 @@ import io.micrometer.common.KeyValues;
import io.micrometer.observation.Observation;
/**
- * {@link Observation.KeyValuesProvider} for Spring Cloud Task.
+ * {@link Observation.ObservationConvention} for Spring Cloud Task.
*
* @author Marcin Grzejszczak
* @since 3.0.0
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationApplicationRunner.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationApplicationRunner.java
index 740bd78f..ef7b1f2a 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationApplicationRunner.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationApplicationRunner.java
@@ -53,6 +53,7 @@ class ObservationApplicationRunner implements ApplicationRunner {
TaskObservationContext context = new TaskObservationContext(this.beanName);
Observation observation = TaskDocumentedObservation.TASK_RUNNER_OBSERVATION.observation(this.taskObservationConvention, INSTANCE, context, registry())
.contextualName(this.beanName);
+
try (Observation.Scope scope = observation.start().openScope()) {
this.delegate.run(args);
}
@@ -71,4 +72,5 @@ class ObservationApplicationRunner implements ApplicationRunner {
}
return this.registry;
}
+
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationTaskAutoConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationTaskAutoConfiguration.java
index 3d3eb2f5..e6cd84b3 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationTaskAutoConfiguration.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/ObservationTaskAutoConfiguration.java
@@ -19,8 +19,6 @@ 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;
@@ -38,7 +36,6 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(ObservationRegistry.class)
@ConditionalOnProperty(value = "spring.cloud.task.observation.enabled", matchIfMissing = true)
@ConditionalOnBean(ObservationRegistry.class)
-@AutoConfigureAfter(ObservationAutoConfiguration.class)
public class ObservationTaskAutoConfiguration {
@Bean
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/TaskObservationConvention.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/TaskObservationConvention.java
index 013d0d2e..5fda2390 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/TaskObservationConvention.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/observation/TaskObservationConvention.java
@@ -19,7 +19,7 @@ package org.springframework.cloud.task.configuration.observation;
import io.micrometer.observation.Observation;
/**
- * {@link Observation.KeyValuesProvider} for Spring Cloud Task.
+ * {@link Observation.ObservationConvention} for Spring Cloud Task.
*
* @author Marcin Grzejszczak
* @since 3.0.0
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/DefaultTaskExecutionObservationConvention.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/DefaultTaskExecutionObservationConvention.java
new file mode 100644
index 00000000..046434d7
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/DefaultTaskExecutionObservationConvention.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022-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.listener;
+
+import io.micrometer.common.KeyValues;
+
+import org.springframework.cloud.task.repository.TaskExecution;
+
+/**
+ * /**
+ * Default {@link TaskExecutionObservationConvention} implementation.
+ *
+ * @author Glenn Renfro
+ * @since 3.0.0
+ */
+public class DefaultTaskExecutionObservationConvention implements TaskExecutionObservationConvention {
+
+ @Override
+ public KeyValues getLowCardinalityKeyValues(TaskExecutionObservationContext context) {
+ return getKeyValuesForTaskExecution(context);
+ }
+
+ @Override
+ public KeyValues getHighCardinalityKeyValues(TaskExecutionObservationContext context) {
+ return KeyValues.empty();
+ }
+
+ private KeyValues getKeyValuesForTaskExecution(TaskExecutionObservationContext context) {
+ TaskExecution execution = context.getTaskExecution();
+ return KeyValues.of(
+ TaskExecutionObservation.TaskKeyValues.TASK_STATUS.getKeyName(), context.getStatus(),
+ TaskExecutionObservation.TaskKeyValues.TASK_EXIT_CODE.getKeyName(), String.valueOf(execution.getExitCode()),
+ TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName(),
+ String.valueOf(execution.getExecutionId()));
+ }
+
+ @Override
+ public String getName() {
+ return "spring.cloud.task";
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservation.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservation.java
new file mode 100644
index 00000000..a1d55277
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservation.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2022-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.listener;
+
+import io.micrometer.common.docs.KeyName;
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.docs.DocumentedObservation;
+
+/**
+ * Enumeration for task execution observations.
+ *
+ * @author Glenn Renfro
+ * @since 3.0.0
+ */
+public enum TaskExecutionObservation implements DocumentedObservation {
+ /**
+ * Metrics created around a task execution.
+ */
+ TASK_ACTIVE {
+
+ @Override
+ public Class extends Observation.ObservationConvention extends Observation.Context>> getDefaultConvention() {
+ return DefaultTaskExecutionObservationConvention.class;
+ }
+
+ public String getPrefix() {
+ return "spring.cloud.task";
+ }
+ };
+ @Override
+ public KeyName[] getLowCardinalityKeyNames() {
+ return TaskKeyValues.values();
+ }
+
+ public enum TaskKeyValues implements KeyName {
+
+ /**
+ * Task name measurement.
+ */
+ TASK_NAME {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.name";
+ }
+ },
+
+ /**
+ * Task execution id.
+ */
+ TASK_EXECUTION_ID {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.execution.id";
+ }
+ },
+
+ /**
+ * Task parent execution id.
+ */
+ TASK_PARENT_EXECUTION_ID {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.parent.execution.id";
+ }
+ },
+
+ /**
+ * External execution id for task.
+ */
+ TASK_EXTERNAL_EXECUTION_ID {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.external.execution.id";
+ }
+ },
+ /**
+ * Task exit code.
+ */
+ TASK_EXIT_CODE {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.exit.code";
+ }
+ },
+
+ /**
+ * task status. Can be either success or failure.
+ */
+ TASK_STATUS {
+ @Override
+ public String getKeyName() {
+ return "spring.cloud.task.status";
+ }
+ },
+
+ /**
+ * Organization Name for CF cloud.
+ */
+ TASK_CF_ORG_NAME {
+ @Override
+ public String getKeyName() {
+ return "cf.org.name";
+ }
+ },
+
+ /**
+ * Space id for CF cloud.
+ */
+ TASK_CF_SPACE_ID {
+ @Override
+ public String getKeyName() {
+ return "cf.space.id";
+ }
+ },
+
+ /**
+ * Space name for CF cloud.
+ */
+ TASK_CF_SPACE_NAME {
+ @Override
+ public String getKeyName() {
+ return "cf.space.name";
+ }
+ },
+
+ /**
+ * App name for CF cloud.
+ */
+ TASK_CF_APP_NAME {
+ @Override
+ public String getKeyName() {
+ return "cf.app.name";
+ }
+ },
+
+ /**
+ * App id for CF cloud.
+ */
+ TASK_CF_APP_ID {
+ @Override
+ public String getKeyName() {
+ return "cf.app.id";
+ }
+ },
+
+ /**
+ * App version for CF cloud.
+ */
+ TASK_CF_APP_VERSION {
+ @Override
+ public String getKeyName() {
+ return "cf.app.version";
+ }
+ },
+
+ /**
+ * Instance index for CF cloud.
+ */
+ TASK_CF_INSTANCE_INDEX {
+ @Override
+ public String getKeyName() {
+ return "cf.instance.index";
+ }
+ }
+
+ }
+
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationContext.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationContext.java
new file mode 100644
index 00000000..41ac8c7e
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationContext.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022-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.listener;
+
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.ObservationHandler;
+
+import org.springframework.cloud.task.repository.TaskExecution;
+
+/**
+ * A mutable holder of the {@link TaskExecution} required by a {@link ObservationHandler}.
+ *
+ * @author Glenn Renfro
+ * @since 3.0.0
+ */
+public class TaskExecutionObservationContext extends Observation.Context {
+ private final TaskExecution taskExecution;
+
+ private String exceptionMessage = "none";
+
+ private String status = "success";
+
+ public TaskExecutionObservationContext(TaskExecution taskExecution) {
+ this.taskExecution = taskExecution;
+ }
+
+ public TaskExecution getTaskExecution() {
+ return taskExecution;
+ }
+
+ public String getExceptionMessage() {
+ return exceptionMessage;
+ }
+
+ public void setExceptionMessage(String exceptionMessage) {
+ this.exceptionMessage = exceptionMessage;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationConvention.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationConvention.java
new file mode 100644
index 00000000..0ede4ff7
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskExecutionObservationConvention.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022-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.listener;
+
+import io.micrometer.observation.Observation;
+
+/**
+ * {@link Observation.ObservationConvention} for {@link TaskExecutionObservationContext}.
+ *
+ * @author Glenn Renfro
+ * @since 3.0.0
+ */
+public interface TaskExecutionObservationConvention extends Observation.ObservationConvention {
+
+ @Override
+ default boolean supportsContext(Observation.Context context) {
+ return context instanceof TaskExecutionObservationContext;
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java
index 0f982124..cf9d048f 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java
@@ -26,6 +26,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.ObservationRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -36,6 +38,7 @@ import org.springframework.boot.ExitCodeEvent;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.cloud.task.configuration.TaskObservationCloudKeyValues;
import org.springframework.cloud.task.configuration.TaskProperties;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.cloud.task.repository.TaskExplorer;
@@ -88,7 +91,7 @@ public class TaskLifecycleListener implements ApplicationListener taskExecutionListenersFromContext;
+ @Autowired(required = false)
+ private Observation.ObservationConvention observationConvention;
+
private List taskExecutionListeners;
private TaskExecution taskExecution;
@@ -132,7 +138,9 @@ public class TaskLifecycleListener implements ApplicationListener startupListenerList = new ArrayList<>(
this.taskExecutionListeners);
@@ -338,7 +347,9 @@ public class TaskLifecycleListener implements ApplicationListener mockVcapServicesFromString(String serviceJson) {
- final Map env = System.getenv();
- return new MockUp() {
- @Mock
- public String getenv(String name) {
- if (name.equalsIgnoreCase("VCAP_SERVICES")) {
- return serviceJson;
- }
- else {
- return name.equalsIgnoreCase("VCAP_APPLICATION")
- ? "{\"instance_id\":\"123\"}" : (String) env.get(name);
- }
- }
-
- @Mock
- public Map getenv() {
- Map finalMap = new HashMap();
- finalMap.putAll(env);
- finalMap.put("VCAP_SERVICES", serviceJson);
- return finalMap;
- }
- };
- }
-
- @SpringBootApplication
- public static class AutoConfigurationApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(AutoConfigurationApplication.class, args);
- }
-
- @Bean
- @ConditionalOnMissingBean
- public SimpleMeterRegistry simpleMeterRegistry(SimpleConfig config, Clock clock) {
- return new SimpleMeterRegistry(config, clock);
- }
-
- @Bean
- @ConditionalOnMissingBean
- public SimpleConfig simpleConfig() {
- return key -> null;
- }
-
- }
-
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/CloudFoundryMicrometerTagsConfigurationTest.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/CloudFoundryMicrometerTagsConfigurationTest.java
deleted file mode 100644
index 010bb9ef..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/CloudFoundryMicrometerTagsConfigurationTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2019-2019 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.micrometer;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.TestPropertySource;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- */
-@Nested
-public class CloudFoundryMicrometerTagsConfigurationTest {
-
- @ActiveProfiles("cloud")
- public static class ActiveCloudProfileDefaultValues extends AbstractMicrometerTest {
-
- @Test
- public void testDefaultTagValues() {
- assertThat(meter.getId().getTag("cf.org.name")).isEqualTo("default");
- assertThat(meter.getId().getTag("cf.space.id")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("cf.space.name")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("cf.app.name")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("cf.app.id")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("cf.app.version")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("cf.instance.index")).isEqualTo("0");
- }
-
- }
-
- @TestPropertySource(properties = { "vcap.application.org_name=PivotalOrg",
- "vcap.application.space_id=SpringSpaceId",
- "vcap.application.space_name=SpringSpace",
- "vcap.application.application_name=App123",
- "vcap.application.application_id=123guid",
- "vcap.application.application_version=2.0",
- "vcap.application.instance_index=123" })
- @ActiveProfiles("cloud")
- public static class ActiveCloudProfile extends AbstractMicrometerTest {
-
- @Test
- public void testPresetTagValues() {
- assertThat(meter.getId().getTag("cf.org.name")).isEqualTo("PivotalOrg");
- assertThat(meter.getId().getTag("cf.space.id")).isEqualTo("SpringSpaceId");
- assertThat(meter.getId().getTag("cf.space.name")).isEqualTo("SpringSpace");
- assertThat(meter.getId().getTag("cf.app.name")).isEqualTo("App123");
- assertThat(meter.getId().getTag("cf.app.id")).isEqualTo("123guid");
- assertThat(meter.getId().getTag("cf.app.version")).isEqualTo("2.0");
- assertThat(meter.getId().getTag("cf.instance.index")).isEqualTo("123");
- }
-
- }
-
- @TestPropertySource(properties = { "vcap.application.org_name=PivotalOrg",
- "vcap.application.space_id=SpringSpaceId",
- "vcap.application.space_name=SpringSpace",
- "vcap.application.application_name=App123",
- "vcap.application.application_id=123guid",
- "vcap.application.application_version=2.0",
- "vcap.application.instance_index=123" })
- public static class InactiveCloudProfile extends AbstractMicrometerTest {
-
- @Test
- public void testDisabledTagValues() {
- assertThat(meter.getId().getTag("cf.org.name")).isNull();
- assertThat(meter.getId().getTag("cf.space.id")).isNull();
- assertThat(meter.getId().getTag("cf.space.name")).isNull();
- assertThat(meter.getId().getTag("cf.app.name")).isNull();
- assertThat(meter.getId().getTag("cf.app.id")).isNull();
- assertThat(meter.getId().getTag("cf.app.version")).isNull();
- assertThat(meter.getId().getTag("cf.instance.index")).isNull();
- }
-
- }
-
- @TestPropertySource(
- properties = { "spring.cloud.task.metrics.cf.tags.enabled=false" })
- @ActiveProfiles("cloud")
- public static class ActiveCloudProfileDisabledProperty extends InactiveCloudProfile {
-
- }
-
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/SpringCloudTaskMicrometerCommonTagsConfigurationTest.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/SpringCloudTaskMicrometerCommonTagsConfigurationTest.java
deleted file mode 100644
index 19f74d62..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/SpringCloudTaskMicrometerCommonTagsConfigurationTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2019-2019 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.micrometer;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.test.context.TestPropertySource;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- */
-@Nested
-public class SpringCloudTaskMicrometerCommonTagsConfigurationTest {
-
- public static class TestDefaultTagValues extends AbstractMicrometerTest {
-
- @Test
- public void testDefaultTagValues() {
- assertThat(meter.getId().getTag("task.name")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("task.execution.id")).isEqualTo("unknown");
- assertThat(meter.getId().getTag("task.parent.execution.id"))
- .isEqualTo("unknown");
- assertThat(meter.getId().getTag("task.external.execution.id"))
- .isEqualTo("unknown");
- }
-
- }
-
- @TestPropertySource(properties = { "spring.cloud.task.name=myTask",
- "spring.cloud.task.executionid=123",
- "spring.cloud.task.parent-execution-id=999",
- "spring.cloud.task.external-execution-id=696" })
- public static class TestPresetTagValues extends AbstractMicrometerTest {
-
- @Test
- public void testPresetTagValues() {
- assertThat(meter.getId().getTag("task.name")).isEqualTo("myTask");
- assertThat(meter.getId().getTag("task.execution.id")).isEqualTo("123");
- assertThat(meter.getId().getTag("task.parent.execution.id")).isEqualTo("999");
- assertThat(meter.getId().getTag("task.external.execution.id"))
- .isEqualTo("696");
- }
-
- }
-
- @TestPropertySource(
- properties = { "spring.cloud.task.metrics.common.tags.enabled=false" })
- public static class TestDisabledTagValues extends AbstractMicrometerTest {
-
- @Test
- public void testDefaultTagValues() {
- assertThat(meter.getId().getTag("task.name")).isNull();
- assertThat(meter.getId().getTag("task.execution.id")).isNull();
- assertThat(meter.getId().getTag("task.parent.execution.id")).isNull();
- assertThat(meter.getId().getTag("task.external.execution.id")).isNull();
- }
-
- }
-
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskMetricsTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskMetricsTests.java
deleted file mode 100644
index 9cb74c3a..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskMetricsTests.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2019-2019 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.micrometer;
-
-import java.util.ArrayList;
-import java.util.Date;
-
-import io.micrometer.core.instrument.LongTaskTimer;
-import io.micrometer.core.instrument.Metrics;
-import io.micrometer.core.instrument.Timer;
-import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.cloud.task.listener.TaskMetrics;
-import org.springframework.cloud.task.repository.TaskExecution;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- */
-public class TaskMetricsTests {
-
- private TaskMetrics taskMetrics;
-
- private SimpleMeterRegistry simpleMeterRegistry;
-
- @BeforeEach
- public void before() {
- Metrics.globalRegistry.getMeters().forEach(Metrics.globalRegistry::remove);
- simpleMeterRegistry = new SimpleMeterRegistry();
- Metrics.addRegistry(simpleMeterRegistry);
- taskMetrics = new TaskMetrics();
- }
-
- @AfterEach
- public void after() {
- Metrics.globalRegistry.getMeters().forEach(Metrics.globalRegistry::remove);
- }
-
- @Test
- public void successfulTask() {
-
- TaskExecution taskExecution = new TaskExecution(123L, 0, "myTask72", new Date(),
- new Date(), null, new ArrayList<>(), null, null, -1L);
-
- // Start Task
- taskMetrics.onTaskStartup(taskExecution);
-
- //// Test Long Task Timer while the task is running.
- LongTaskTimer longTaskTimer = simpleMeterRegistry
- .find(TaskMetrics.SPRING_CLOUD_TASK_ACTIVE_METER).longTaskTimer();
- assertThat(longTaskTimer)
- .withFailMessage("LongTask timer should be created on Task start")
- .isNotNull();
- // assertThat(longTaskTimer.activeTasks()).isEqualTo(1);
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask72");
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
-
- // Finish Task
- taskMetrics.onTaskEnd(taskExecution);
-
- // Test Timer
- Timer taskTimer = simpleMeterRegistry.find(TaskMetrics.SPRING_CLOUD_TASK_METER)
- .timer();
- assertThat(taskTimer).isNotNull();
- // assertThat(taskTimer.count()).isEqualTo(1L);
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask72");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXTERNAL_EXECUTION_ID_TAG))
- .isEqualTo("unknown");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_PARENT_EXECUTION_ID_TAG))
- .isEqualTo("-1");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXIT_CODE_TAG))
- .isEqualTo("0");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXCEPTION_TAG))
- .isEqualTo("none");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_STATUS_TAG))
- .isEqualTo(TaskMetrics.STATUS_SUCCESS);
-
- // Test Long Task Timer after the task has completed.
- // LongTaskTimer longTaskTimer = simpleMeterRegistry
- // .find(TaskMetrics.SPRING_CLOUD_TASK_ACTIVE_METER).longTaskTimer();
- assertThat(longTaskTimer.activeTasks()).isEqualTo(0);
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask72");
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
- }
-
- @Test
- public void failingTask() {
-
- TaskExecution taskExecution = new TaskExecution(123L, 0, "myTask", new Date(),
- new Date(), null, new ArrayList<>(), null, null, -1L);
-
- // Start Task
- taskMetrics.onTaskStartup(taskExecution);
-
- // Test Long Task Timer while the task is running.
- LongTaskTimer longTaskTimer = simpleMeterRegistry
- .find(TaskMetrics.SPRING_CLOUD_TASK_ACTIVE_METER).longTaskTimer();
- assertThat(longTaskTimer)
- .withFailMessage("LongTask timer should be created on Task start")
- .isNotNull();
- assertThat(longTaskTimer.activeTasks()).isEqualTo(1);
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask");
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
-
- taskMetrics.onTaskFailed(new RuntimeException("Test"));
-
- // Finish Task. TaskLifecycleListen calls onTaskEnd after the onTaskFailed. Make
- // sure that the counter status
- // is not affected by this.
- taskMetrics.onTaskEnd(taskExecution);
-
- Timer taskTimer = simpleMeterRegistry.find(TaskMetrics.SPRING_CLOUD_TASK_METER)
- .timer();
- assertThat(taskTimer).isNotNull();
-
- assertThat(taskTimer).isNotNull();
- // assertThat(taskTimer.count()).isEqualTo(1L);
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXTERNAL_EXECUTION_ID_TAG))
- .isEqualTo("unknown");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_PARENT_EXECUTION_ID_TAG))
- .isEqualTo("-1");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXIT_CODE_TAG))
- .isEqualTo("0");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_EXCEPTION_TAG))
- .isEqualTo("RuntimeException");
- assertThat(taskTimer.getId().getTag(TaskMetrics.TASK_STATUS_TAG))
- .isEqualTo(TaskMetrics.STATUS_FAILURE);
-
- // Test Long Task Timer after the task has completed.
- assertThat(longTaskTimer.activeTasks()).isEqualTo(0);
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_NAME_TAG))
- .isEqualTo("myTask");
- assertThat(longTaskTimer.getId().getTag(TaskMetrics.TASK_EXECUTION_ID_TAG))
- .isEqualTo("123");
- }
-
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskObservationsTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskObservationsTests.java
new file mode 100644
index 00000000..79baa513
--- /dev/null
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/micrometer/TaskObservationsTests.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2019-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.micrometer;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import io.micrometer.core.instrument.LongTaskTimer;
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.observation.TimerObservationHandler;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import io.micrometer.core.tck.MeterRegistryAssert;
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.ObservationHandler;
+import io.micrometer.observation.ObservationRegistry;
+import io.micrometer.observation.tck.ObservationRegistryAssert;
+import io.micrometer.observation.tck.TestObservationRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.cloud.task.configuration.TaskObservationCloudKeyValues;
+import org.springframework.cloud.task.listener.TaskExecutionObservation;
+import org.springframework.cloud.task.listener.TaskObservations;
+import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.cloud.task.listener.TaskObservations.UNKNOWN;
+
+/**
+ * @author Christian Tzolov
+ * @author Glenn Renfro
+ */
+public class TaskObservationsTests {
+
+ public static final String PREFIX = "spring.cloud.task";
+
+ private TaskObservations taskObservations;
+
+ private SimpleMeterRegistry simpleMeterRegistry;
+
+ private ObservationRegistry observationRegistry;
+
+ @BeforeEach
+ public void before() {
+ this.simpleMeterRegistry = new SimpleMeterRegistry();
+ this.observationRegistry = TestObservationRegistry.create();
+ ObservationHandler timerObservationHandler = new TimerObservationHandler(this.simpleMeterRegistry);
+ this.observationRegistry.observationConfig().observationHandler(timerObservationHandler);
+ this.taskObservations = new TaskObservations(this.observationRegistry, null, null);
+ }
+
+ @AfterEach
+ public void after() {
+ this.simpleMeterRegistry.clear();
+ ObservationRegistryAssert.assertThat(this.observationRegistry).doesNotHaveAnyRemainingCurrentObservation();
+ }
+
+ @Test
+ public void successfulTaskTest() {
+
+ TaskExecution taskExecution = startupObservationForBasicTests("myTask72", 123L);
+
+ LongTaskTimer longTaskTimer = initializeBasicTest("myTask72", "123");
+
+ // Finish Task
+ taskObservations.onTaskEnd(taskExecution);
+
+ verifyDefaultKeyValues();
+ TaskExecutionObservation.TASK_ACTIVE.getDefaultConvention();
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags("spring.cloud.task",
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_STATUS.getKeyName(), TaskObservations.STATUS_SUCCESS));
+
+ verifyLongTaskTimerAfterStop(longTaskTimer, "myTask72", "123");
+ }
+
+ @Test
+ public void defaultTaskTest() {
+
+ TaskExecution taskExecution = new TaskExecution(123L, 0, null, new Date(),
+ new Date(), null, new ArrayList<>(), null, null, null);
+
+ // Start Task
+ taskObservations.onTaskStartup(taskExecution);
+
+ LongTaskTimer longTaskTimer = initializeBasicTest(UNKNOWN, "123");
+
+
+ // Finish Task
+ taskObservations.onTaskEnd(taskExecution);
+
+ // Test Timer
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName(), "123"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_PARENT_EXECUTION_ID.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXTERNAL_EXECUTION_ID.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXIT_CODE.getKeyName(), "0"));
+
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_STATUS.getKeyName(), TaskObservations.STATUS_SUCCESS));
+
+ verifyLongTaskTimerAfterStop(longTaskTimer, "unknown", "123");
+
+ }
+
+ @Test
+ public void failingTask() {
+
+ TaskExecution taskExecution = startupObservationForBasicTests("myTask72", 123L);
+
+ LongTaskTimer longTaskTimer = initializeBasicTest("myTask72", "123");
+
+ taskObservations.onTaskFailed(new RuntimeException("Test"));
+
+ // Finish Task. TaskLifecycleListen calls onTaskEnd after the onTaskFailed. Make
+ // sure that the counter status
+ // is not affected by this.
+ taskExecution.setExitCode(1);
+ taskObservations.onTaskEnd(taskExecution);
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), "myTask72"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName(), "123"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_PARENT_EXECUTION_ID.getKeyName(), "-1"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXIT_CODE.getKeyName(), "1"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_STATUS.getKeyName(), TaskObservations.STATUS_FAILURE));
+
+ verifyLongTaskTimerAfterStop(longTaskTimer, "myTask72", "123");
+ }
+
+ @Test
+ public void taskWithCloudKeyValues() {
+ final String APPLICATION_ID = "123";
+ final String APPLICATION_NAME = "APP123";
+ final String SPACE_ID = "123";
+ final String SPACE_NAME = "SPACE123";
+ final String APPLICATION_VERSION = "APPV123";
+ final String INSTANCE_INDEX = "55";
+ final String ORGANIZATION_NAME = "ORG123";
+ TaskObservationCloudKeyValues taskObservationCloudKeyValues = new TaskObservationCloudKeyValues();
+ taskObservationCloudKeyValues.setApplicationId(APPLICATION_ID);
+ taskObservationCloudKeyValues.setApplicationName(APPLICATION_NAME);
+ taskObservationCloudKeyValues.setSpaceId(SPACE_ID);
+ taskObservationCloudKeyValues.setSpaceName(SPACE_NAME);
+ taskObservationCloudKeyValues.setApplicationVersion(APPLICATION_VERSION);
+ taskObservationCloudKeyValues.setInstanceIndex(INSTANCE_INDEX);
+ taskObservationCloudKeyValues.setOrganizationName(ORGANIZATION_NAME);
+ this.taskObservations = new TaskObservations(this.observationRegistry, taskObservationCloudKeyValues, null);
+
+ TaskExecution taskExecution = startupObservationForBasicTests("myTask72", 123L);
+
+ LongTaskTimer longTaskTimer = initializeBasicTest("myTask72", "123");
+
+ // Finish Task
+ taskObservations.onTaskEnd(taskExecution);
+
+ verifyDefaultKeyValues();
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_ORG_NAME.getKeyName(), ORGANIZATION_NAME));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_ID.getKeyName(), SPACE_ID));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_NAME.getKeyName(), SPACE_NAME));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_NAME.getKeyName(), APPLICATION_NAME));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_ID.getKeyName(), APPLICATION_ID));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_VERSION.getKeyName(), APPLICATION_VERSION));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_INSTANCE_INDEX.getKeyName(), INSTANCE_INDEX));
+
+ // Test Timer
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), "myTask72"));
+
+ verifyLongTaskTimerAfterStop(longTaskTimer, "myTask72", "123");
+ }
+
+ @Test
+ public void testCloudVariablesUninitialized() {
+ ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(
+ CloudConfigurationForDefaultValues.class));
+ applicationContextRunner.run((context) -> {
+ TaskObservationCloudKeyValues taskObservationCloudKeyValues = context
+ .getBean(TaskObservationCloudKeyValues.class);
+
+ assertThat(taskObservationCloudKeyValues)
+ .as("taskObservationCloudKeyValues should not be null").isNotNull();
+
+ this.taskObservations = new TaskObservations(this.observationRegistry, taskObservationCloudKeyValues, null);
+
+ TaskExecution taskExecution = startupObservationForBasicTests("myTask72", 123L);
+
+ LongTaskTimer longTaskTimer = initializeBasicTest("myTask72", "123");
+
+ // Finish Task
+ taskObservations.onTaskEnd(taskExecution);
+
+ verifyDefaultKeyValues();
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_ORG_NAME.getKeyName(), "default"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_ID.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_NAME.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_NAME.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_ID.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_VERSION.getKeyName(), UNKNOWN));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_CF_INSTANCE_INDEX.getKeyName(), "0"));
+
+ // Test Timer
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), "myTask72"));
+
+ verifyLongTaskTimerAfterStop(longTaskTimer, "myTask72", "123");
+ });
+ }
+
+ private TaskExecution startupObservationForBasicTests(String taskName, long taskExecutionId) {
+ TaskExecution taskExecution = new TaskExecution(taskExecutionId, 0, taskName, new Date(),
+ new Date(), null, new ArrayList<>(), null, "-1", -1L);
+
+ // Start Task
+ taskObservations.onTaskStartup(taskExecution);
+ return taskExecution;
+ }
+
+ private LongTaskTimer initializeBasicTest(String taskName, String executionId) {
+ // Test Long Task Timer while the task is running.
+ LongTaskTimer longTaskTimer = simpleMeterRegistry
+ .find(TaskExecutionObservation.TASK_ACTIVE.getPrefix() + ".active").longTaskTimer();
+ System.out.println(simpleMeterRegistry.getMetersAsString());
+ assertThat(longTaskTimer)
+ .withFailMessage("LongTask timer should be created on Task start")
+ .isNotNull();
+ assertThat(longTaskTimer.activeTasks()).isEqualTo(1);
+ assertThat(longTaskTimer.getId().getTag(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName()))
+ .isEqualTo(taskName);
+ assertThat(longTaskTimer.getId().getTag(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName()))
+ .isEqualTo(executionId);
+ return longTaskTimer;
+ }
+
+ private void verifyDefaultKeyValues() {
+ // Test Timer
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), "myTask72"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName(), "123"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_PARENT_EXECUTION_ID.getKeyName(), "-1"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_EXIT_CODE.getKeyName(), "0"));
+
+ MeterRegistryAssert.assertThat(this.simpleMeterRegistry)
+ .hasTimerWithNameAndTags(PREFIX,
+ Tags.of(TaskExecutionObservation.TaskKeyValues.TASK_STATUS.getKeyName(), TaskObservations.STATUS_SUCCESS));
+ }
+
+ private void verifyLongTaskTimerAfterStop(LongTaskTimer longTaskTimer, String taskName, String executionId) {
+ // Test Long Task Timer after the task has completed.
+ assertThat(longTaskTimer.activeTasks()).isEqualTo(0);
+ assertThat(longTaskTimer.getId().getTag(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName()))
+ .isEqualTo(taskName);
+ assertThat(longTaskTimer.getId().getTag(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName()))
+ .isEqualTo(executionId);
+ }
+
+ @Configuration
+ static class CloudConfigurationForDefaultValues {
+ @Bean
+ public TaskObservationCloudKeyValues taskObservationCloudKeyValues() {
+ return new TaskObservationCloudKeyValues();
+ }
+ }
+}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
index 0d9f1fd7..0c5e5ee8 100644
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
@@ -18,10 +18,13 @@ package org.springframework.cloud.task.util;
import javax.sql.DataSource;
+import io.micrometer.observation.ObservationRegistry;
+
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.task.configuration.TaskObservationCloudKeyValues;
import org.springframework.cloud.task.configuration.TaskProperties;
import org.springframework.cloud.task.listener.TaskLifecycleListener;
import org.springframework.cloud.task.listener.TaskListenerExecutorObjectFactory;
@@ -82,10 +85,12 @@ public class TestDefaultConfiguration implements InitializingBean {
}
@Bean
- public TaskLifecycleListener taskHandler(TaskExplorer taskExplorer) {
+ public TaskLifecycleListener taskHandler(TaskExplorer taskExplorer,
+ @Autowired(required = false) io.micrometer.core.instrument.MeterRegistry meterRegistry, @Autowired(required = false) ObservationRegistry observationRegistry) {
+
return new TaskLifecycleListener(taskRepository(), taskNameResolver(),
this.applicationArguments, taskExplorer, this.taskProperties,
- taskListenerExecutorObjectProvider(this.context));
+ taskListenerExecutorObjectProvider(this.context), observationRegistry, new TaskObservationCloudKeyValues());
}
@Override
diff --git a/spring-cloud-task-samples/pom.xml b/spring-cloud-task-samples/pom.xml
index 36375e8a..ca437c49 100644
--- a/spring-cloud-task-samples/pom.xml
+++ b/spring-cloud-task-samples/pom.xml
@@ -28,7 +28,7 @@
task-events
batch-events
jpa-sample
- task-metrics
+ task-observations
multiple-datasources
single-step-batch-job
diff --git a/spring-cloud-task-samples/task-metrics/src/main/resources/application.properties b/spring-cloud-task-samples/task-metrics/src/main/resources/application.properties
deleted file mode 100644
index e8aac3b6..00000000
--- a/spring-cloud-task-samples/task-metrics/src/main/resources/application.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-logging.level.org.springframework.cloud.task=debug
-management.metrics.tags.service=task-metrics-application
-management.metrics.tags.application=task-metrics-application-58
-spring.cloud.task.name=taskmetrics
-spring.cloud.task.observation.enabled=true
-
diff --git a/spring-cloud-task-samples/task-metrics/.gitignore b/spring-cloud-task-samples/task-observations/.gitignore
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/.gitignore
rename to spring-cloud-task-samples/task-observations/.gitignore
diff --git a/spring-cloud-task-samples/task-metrics/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-task-samples/task-observations/.mvn/wrapper/maven-wrapper.jar
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/.mvn/wrapper/maven-wrapper.jar
rename to spring-cloud-task-samples/task-observations/.mvn/wrapper/maven-wrapper.jar
diff --git a/spring-cloud-task-samples/task-metrics/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-task-samples/task-observations/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/.mvn/wrapper/maven-wrapper.properties
rename to spring-cloud-task-samples/task-observations/.mvn/wrapper/maven-wrapper.properties
diff --git a/spring-cloud-task-samples/task-metrics/README.adoc b/spring-cloud-task-samples/task-observations/README.adoc
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/README.adoc
rename to spring-cloud-task-samples/task-observations/README.adoc
diff --git a/spring-cloud-task-samples/task-metrics/mvnw b/spring-cloud-task-samples/task-observations/mvnw
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/mvnw
rename to spring-cloud-task-samples/task-observations/mvnw
diff --git a/spring-cloud-task-samples/task-metrics/mvnw.cmd b/spring-cloud-task-samples/task-observations/mvnw.cmd
similarity index 100%
rename from spring-cloud-task-samples/task-metrics/mvnw.cmd
rename to spring-cloud-task-samples/task-observations/mvnw.cmd
diff --git a/spring-cloud-task-samples/task-metrics/pom.xml b/spring-cloud-task-samples/task-observations/pom.xml
similarity index 95%
rename from spring-cloud-task-samples/task-metrics/pom.xml
rename to spring-cloud-task-samples/task-observations/pom.xml
index 6ab33d15..5aeca82c 100644
--- a/spring-cloud-task-samples/task-metrics/pom.xml
+++ b/spring-cloud-task-samples/task-observations/pom.xml
@@ -4,10 +4,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
io.spring
- task-metrics
+ task-observations
3.0.0-SNAPSHOT
- task-metrics
- task-metrics
+ task observation sample
+ Displays task observations as well as commandline and application runner observations
17
2022.0.0-SNAPSHOT
diff --git a/spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/MetricConfiguration.java b/spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/ObservationConfiguration.java
similarity index 92%
rename from spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/MetricConfiguration.java
rename to spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/ObservationConfiguration.java
index 750af882..fe8e21a6 100644
--- a/spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/MetricConfiguration.java
+++ b/spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/ObservationConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.spring.taskmetrics;
+package io.spring.taskobservations;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
@@ -22,7 +22,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
-public class MetricConfiguration {
+public class ObservationConfiguration {
@Bean
public SimpleMeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
diff --git a/spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/TaskMetricsApplication.java b/spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/TaskObservationsApplication.java
similarity index 74%
rename from spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/TaskMetricsApplication.java
rename to spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/TaskObservationsApplication.java
index 90cd38ce..8da7ee60 100644
--- a/spring-cloud-task-samples/task-metrics/src/main/java/io/spring/taskmetrics/TaskMetricsApplication.java
+++ b/spring-cloud-task-samples/task-observations/src/main/java/io/spring/taskobservations/TaskObservationsApplication.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package io.spring.taskmetrics;
+package io.spring.taskobservations;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
@@ -33,35 +32,25 @@ import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableTask
-public class TaskMetricsApplication {
+public class TaskObservationsApplication {
- private static final Log logger = LogFactory.getLog(TaskMetricsApplication.class);
+ private static final Log logger = LogFactory.getLog(TaskObservationsApplication.class);
@Autowired
public SimpleMeterRegistry simpleMeterRegistry;
public static void main(String[] args) {
- SpringApplication.run(TaskMetricsApplication.class, args);
+ SpringApplication.run(TaskObservationsApplication.class, args);
}
@Bean
public ApplicationRunner applicationRunner() {
- return new ApplicationRunner() {
- @Override
- public void run(ApplicationArguments args) throws Exception {
- logger.info("Hello ApplicationRunner Metric's World");
- }
- };
+ return args -> logger.info("Hello ApplicationRunner Metric's World");
}
@Bean
public CommandLineRunner commandLineRunner() {
- return new CommandLineRunner() {
- @Override
- public void run(String... args) throws Exception {
- logger.info("Hello CommandLineRunner Metric's World");
- }
- };
+ return args -> logger.info("Hello CommandLineRunner Metric's World");
}
/**
diff --git a/spring-cloud-task-samples/task-observations/src/main/resources/application.properties b/spring-cloud-task-samples/task-observations/src/main/resources/application.properties
new file mode 100644
index 00000000..678c8937
--- /dev/null
+++ b/spring-cloud-task-samples/task-observations/src/main/resources/application.properties
@@ -0,0 +1,6 @@
+logging.level.org.springframework.cloud.task=debug
+management.metrics.tags.service=task-observations-application
+management.metrics.tags.application=task-observations-application-58
+spring.cloud.task.name=taskmetrics
+spring.cloud.task.observation.enabled=true
+
diff --git a/spring-cloud-task-samples/task-metrics/src/test/java/io/spring/taskmetrics/TaskMetricsApplicationTests.java b/spring-cloud-task-samples/task-observations/src/test/java/io/spring/taskobservations/TaskObservationsApplicationTests.java
similarity index 71%
rename from spring-cloud-task-samples/task-metrics/src/test/java/io/spring/taskmetrics/TaskMetricsApplicationTests.java
rename to spring-cloud-task-samples/task-observations/src/test/java/io/spring/taskobservations/TaskObservationsApplicationTests.java
index e746ead9..938f32fe 100644
--- a/spring-cloud-task-samples/task-metrics/src/test/java/io/spring/taskmetrics/TaskMetricsApplicationTests.java
+++ b/spring-cloud-task-samples/task-observations/src/test/java/io/spring/taskobservations/TaskObservationsApplicationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.spring.taskmetrics;
+package io.spring.taskobservations;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -27,14 +27,17 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@SpringBootTest
@ExtendWith(OutputCaptureExtension.class)
-class TaskMetricsApplicationTests {
+class TaskObservationsApplicationTests {
@Test
void contextLoads(CapturedOutput output) {
String result = output.getAll();
- assertThat(result).contains("spring.cloud.task(TIMER)[application=" +
- "'task-metrics-application-58', service='task-metrics-application', " +
- "task.exception='none', task.execution.id='");
+ assertThat(result).contains("spring.cloud.task(TIMER)[application='task-observations-application-58', " +
+ "error='none', service='task-observations-application', " +
+ "spring.cloud.task.execution.id='1', spring.cloud.task.exit.code='0', " +
+ "spring.cloud.task.external.execution.id='unknown', spring.cloud.task.name='taskmetrics', " +
+ "spring.cloud.task.parent.execution.id='unknown', spring.cloud.task.status='success']; " +
+ "count=1.0, total_time=");
}
}
diff --git a/spring-cloud-task-stream/pom.xml b/spring-cloud-task-stream/pom.xml
index 1be77a3b..198db76e 100644
--- a/spring-cloud-task-stream/pom.xml
+++ b/spring-cloud-task-stream/pom.xml
@@ -48,6 +48,7 @@
org.springframework.cloud
spring-cloud-task-core
+
org.springframework.boot
spring-boot-starter-test