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> 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