Updating task metrics to micrometer 1.10
Changes include: * Use Observable instead of metrics * Update the setting of lowCardinalityTag to the correct events. * Update the docs * Remove EXTERNAL_EXECUTION_ID * Utilize registry provided by boot instead of globalregistry * Set the handler until Boot does this as shown on line 80 of TaskLifecycleConfiguration. * Uses Observability long task timer Updated to latest changes in Micrometer Observability Updated based on code review and api change Finished removing the metrics registry Updated based on code review Readded cloud foundry keys to observations Renamed metrics to observations where applicable Updated metric sample to be observation sample Updated to set defaults and migrate tests to infrastructure Updated based on code review Remove duplicate keyvalues from task observation Added DocumentedObservation to create the observation Migrated code to use the LowerCardinality KeyNames from the DocumentedObservation Test cleanup Updated code based on code review Exception is handled by the error in the observation vs logging it as a task metric key value Updated to use ObservationConvention Updated to handle user defined convention vs default Removed the snapshot versions of observations Removed unnecessary dependencies Checkpoint tests fail after re-adding 1.10-SNAPSHOT Replaced TimeObservationHandler with DefaultMeterObservationHandler Updated to set micrometer to latest milestone Updated to rename TaskValuesProvider variables to ObservationConvention
This commit is contained in:
@@ -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]
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-influx</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Task Core</name>
|
||||
<description>Spring Cloud Task</description>
|
||||
<properties>
|
||||
<jmock-version>1.49</jmock-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -123,19 +120,8 @@
|
||||
<artifactId>micrometer-observation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jmockit</groupId>
|
||||
<artifactId>jmockit</artifactId>
|
||||
<version>${jmock-version}</version>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -144,8 +130,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-test</artifactId>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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> applicationArguments) {
|
||||
ConfigurableApplicationContext context, TaskRepository taskRepository,
|
||||
TaskExplorer taskExplorer, TaskNameResolver taskNameResolver,
|
||||
ObjectProvider<ApplicationArguments> 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<TaskExecutionObservationContext> {
|
||||
|
||||
@Override
|
||||
default boolean supportsContext(Observation.Context context) {
|
||||
return context instanceof TaskExecutionObservationContext;
|
||||
}
|
||||
}
|
||||
@@ -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<ApplicationEve
|
||||
|
||||
private final TaskListenerExecutorObjectFactory taskListenerExecutorObjectFactory;
|
||||
|
||||
private final TaskMetrics taskMetrics;
|
||||
private final TaskObservations taskObservations;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
@@ -96,6 +99,9 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
@Autowired(required = false)
|
||||
private Collection<TaskExecutionListener> taskExecutionListenersFromContext;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Observation.ObservationConvention observationConvention;
|
||||
|
||||
private List<TaskExecutionListener> taskExecutionListeners;
|
||||
|
||||
private TaskExecution taskExecution;
|
||||
@@ -132,7 +138,9 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
public TaskLifecycleListener(TaskRepository taskRepository,
|
||||
TaskNameResolver taskNameResolver, ApplicationArguments applicationArguments,
|
||||
TaskExplorer taskExplorer, TaskProperties taskProperties,
|
||||
TaskListenerExecutorObjectFactory taskListenerExecutorObjectFactory) {
|
||||
TaskListenerExecutorObjectFactory taskListenerExecutorObjectFactory,
|
||||
@Autowired(required = false) ObservationRegistry observationRegistry,
|
||||
TaskObservationCloudKeyValues taskObservationCloudKeyValues) {
|
||||
Assert.notNull(taskRepository, "A taskRepository is required");
|
||||
Assert.notNull(taskNameResolver, "A taskNameResolver is required");
|
||||
Assert.notNull(taskExplorer, "A taskExplorer is required");
|
||||
@@ -146,7 +154,8 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
this.taskExplorer = taskExplorer;
|
||||
this.taskProperties = taskProperties;
|
||||
this.taskListenerExecutorObjectFactory = taskListenerExecutorObjectFactory;
|
||||
this.taskMetrics = new TaskMetrics();
|
||||
observationRegistry = observationRegistry == null ? ObservationRegistry.NOOP : observationRegistry;
|
||||
this.taskObservations = new TaskObservations(observationRegistry, taskObservationCloudKeyValues, observationConvention);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,7 +324,7 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
}
|
||||
|
||||
private TaskExecution invokeOnTaskStartup(TaskExecution taskExecution) {
|
||||
this.taskMetrics.onTaskStartup(taskExecution);
|
||||
this.taskObservations.onTaskStartup(taskExecution);
|
||||
TaskExecution listenerTaskExecution = getTaskExecutionCopy(taskExecution);
|
||||
List<TaskExecutionListener> startupListenerList = new ArrayList<>(
|
||||
this.taskExecutionListeners);
|
||||
@@ -338,7 +347,9 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
}
|
||||
|
||||
private TaskExecution invokeOnTaskEnd(TaskExecution taskExecution) {
|
||||
this.taskMetrics.onTaskEnd(taskExecution);
|
||||
if (this.taskObservations != null) {
|
||||
this.taskObservations.onTaskEnd(taskExecution);
|
||||
}
|
||||
TaskExecution listenerTaskExecution = getTaskExecutionCopy(taskExecution);
|
||||
if (this.taskExecutionListeners != null) {
|
||||
try {
|
||||
@@ -362,7 +373,9 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
|
||||
|
||||
private TaskExecution invokeOnTaskError(TaskExecution taskExecution,
|
||||
Throwable throwable) {
|
||||
this.taskMetrics.onTaskFailed(throwable);
|
||||
if (this.taskObservations != null) {
|
||||
this.taskObservations.onTaskFailed(throwable);
|
||||
}
|
||||
TaskExecution listenerTaskExecution = getTaskExecutionCopy(taskExecution);
|
||||
if (this.taskExecutionListeners != null) {
|
||||
try {
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import io.micrometer.core.instrument.LongTaskTimer;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
|
||||
import org.springframework.cloud.task.repository.TaskExecution;
|
||||
|
||||
/**
|
||||
* Utility class for publishing Spring Cloud Task specific metrics via Micrometer.
|
||||
* Intended for internal use only.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @since 2.2
|
||||
*/
|
||||
public class TaskMetrics {
|
||||
|
||||
/**
|
||||
* Task timer measurements. Records information about task duration and status.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_TASK_METER = "spring.cloud.task";
|
||||
|
||||
/**
|
||||
* LongTask timer measurement. Records the run-time status of long-time lasting tasks.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_TASK_ACTIVE_METER = "spring.cloud.task.active";
|
||||
|
||||
/**
|
||||
* Successful task execution status indicator.
|
||||
*/
|
||||
public static final String STATUS_SUCCESS = "success";
|
||||
|
||||
/**
|
||||
* Failing task execution status indicator.
|
||||
*/
|
||||
public static final String STATUS_FAILURE = "failure";
|
||||
|
||||
/**
|
||||
* task name measurement tag.
|
||||
*/
|
||||
public static final String TASK_NAME_TAG = "task.name";
|
||||
|
||||
/**
|
||||
* task execution id tag.
|
||||
*/
|
||||
public static final String TASK_EXECUTION_ID_TAG = "task.execution.id";
|
||||
|
||||
/**
|
||||
* task parent execution id tag.
|
||||
*/
|
||||
public static final String TASK_PARENT_EXECUTION_ID_TAG = "task.parent.execution.id";
|
||||
|
||||
/**
|
||||
* task external execution id tag.
|
||||
*/
|
||||
public static final String TASK_EXTERNAL_EXECUTION_ID_TAG = "task.external.execution.id";
|
||||
|
||||
/**
|
||||
* task exit code tag.
|
||||
*/
|
||||
public static final String TASK_EXIT_CODE_TAG = "task.exit.code";
|
||||
|
||||
/**
|
||||
* task status tag. Can be either STATUS_SUCCESS or STATUS_FAILURE
|
||||
*/
|
||||
public static final String TASK_STATUS_TAG = "task.status";
|
||||
|
||||
/**
|
||||
* task exception tag. Contains the name of the exception class in case of error or
|
||||
* none otherwise.
|
||||
*/
|
||||
public static final String TASK_EXCEPTION_TAG = "task.exception";
|
||||
|
||||
private Timer.Sample taskSample;
|
||||
|
||||
private LongTaskTimer.Sample longTaskSample;
|
||||
|
||||
private Throwable exception;
|
||||
|
||||
public void onTaskStartup(TaskExecution taskExecution) {
|
||||
LongTaskTimer longTaskTimer = LongTaskTimer
|
||||
.builder(SPRING_CLOUD_TASK_ACTIVE_METER).description("Long task duration")
|
||||
.tags(commonTags(taskExecution)).register(Metrics.globalRegistry);
|
||||
|
||||
this.longTaskSample = longTaskTimer.start();
|
||||
this.taskSample = Timer.start(Metrics.globalRegistry);
|
||||
}
|
||||
|
||||
public void onTaskFailed(Throwable throwable) {
|
||||
this.exception = throwable;
|
||||
}
|
||||
|
||||
public void onTaskEnd(TaskExecution taskExecution) {
|
||||
if (this.taskSample != null) {
|
||||
this.taskSample.stop(Timer.builder(SPRING_CLOUD_TASK_METER)
|
||||
.description("Task duration").tags(commonTags(taskExecution))
|
||||
.tag(TASK_EXIT_CODE_TAG, "" + taskExecution.getExitCode())
|
||||
.tag(TASK_EXCEPTION_TAG,
|
||||
(this.exception == null) ? "none"
|
||||
: this.exception.getClass().getSimpleName())
|
||||
.tag(TASK_STATUS_TAG,
|
||||
(this.exception == null) ? STATUS_SUCCESS : STATUS_FAILURE).register(Metrics.globalRegistry));
|
||||
this.taskSample = null;
|
||||
}
|
||||
|
||||
if (this.longTaskSample != null) {
|
||||
this.longTaskSample.stop();
|
||||
this.longTaskSample = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Tags commonTags(TaskExecution taskExecution) {
|
||||
return Tags.of(TASK_NAME_TAG, taskExecution.getTaskName())
|
||||
.and(TASK_EXECUTION_ID_TAG, "" + taskExecution.getExecutionId())
|
||||
.and(TASK_PARENT_EXECUTION_ID_TAG,
|
||||
"" + taskExecution.getParentExecutionId())
|
||||
.and(TASK_EXTERNAL_EXECUTION_ID_TAG,
|
||||
(taskExecution.getExternalExecutionId() == null) ? "unknown"
|
||||
: "" + taskExecution.getExternalExecutionId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.cloud.task.configuration.TaskObservationCloudKeyValues;
|
||||
import org.springframework.cloud.task.repository.TaskExecution;
|
||||
|
||||
/**
|
||||
* Utility class for publishing Spring Cloud Task specific Observations via Micrometer.
|
||||
* Intended for internal use only.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Glenn Renfro
|
||||
* @since 2.2
|
||||
*/
|
||||
public class TaskObservations {
|
||||
|
||||
/**
|
||||
* Successful task execution status indicator.
|
||||
*/
|
||||
public static final String STATUS_SUCCESS = "success";
|
||||
|
||||
/**
|
||||
* Failing task execution status indicator.
|
||||
*/
|
||||
public static final String STATUS_FAILURE = "failure";
|
||||
|
||||
/**
|
||||
* Default for when value is not present.
|
||||
*/
|
||||
public static final String UNKNOWN = "unknown";
|
||||
|
||||
private ObservationRegistry observationRegistry;
|
||||
|
||||
private Observation.ObservationConvention customObservationConvention;
|
||||
|
||||
public TaskObservations(ObservationRegistry observationRegistry, TaskObservationCloudKeyValues taskObservationCloudKeyValues,
|
||||
Observation.ObservationConvention customObservationConvention) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.taskObservationCloudKeyValues = taskObservationCloudKeyValues;
|
||||
this.customObservationConvention = customObservationConvention;
|
||||
}
|
||||
|
||||
private Observation.Scope scope;
|
||||
|
||||
private TaskExecutionObservationConvention observationsProvider = new DefaultTaskExecutionObservationConvention();
|
||||
|
||||
private TaskExecutionObservationContext taskObservationContext;
|
||||
|
||||
TaskObservationCloudKeyValues taskObservationCloudKeyValues;
|
||||
|
||||
public void onTaskStartup(TaskExecution taskExecution) {
|
||||
|
||||
|
||||
this.taskObservationContext = new TaskExecutionObservationContext(taskExecution);
|
||||
|
||||
Observation observation = TaskExecutionObservation.TASK_ACTIVE.observation(this.customObservationConvention, new DefaultTaskExecutionObservationConvention(), this.taskObservationContext, this.observationRegistry)
|
||||
.contextualName(String.valueOf(taskExecution.getExecutionId()))
|
||||
.keyValuesProvider(this.observationsProvider)
|
||||
.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_NAME.getKeyName(), getValueOrDefault(taskExecution.getTaskName()))
|
||||
.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_EXECUTION_ID.getKeyName(), "" + taskExecution.getExecutionId())
|
||||
.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_PARENT_EXECUTION_ID.getKeyName(),
|
||||
(getValueOrDefault(taskExecution.getParentExecutionId())))
|
||||
.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_EXTERNAL_EXECUTION_ID.getKeyName(),
|
||||
(getValueOrDefault(taskExecution.getExternalExecutionId())));
|
||||
|
||||
if (taskObservationCloudKeyValues != null) {
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_ORG_NAME.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getOrganizationName());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_ID.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getSpaceId());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_SPACE_NAME.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getSpaceName());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_ID.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getApplicationId());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_NAME.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getApplicationName());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_APP_VERSION.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getApplicationVersion());
|
||||
observation.lowCardinalityKeyValue(TaskExecutionObservation.TaskKeyValues.TASK_CF_INSTANCE_INDEX.getKeyName(),
|
||||
this.taskObservationCloudKeyValues.getInstanceIndex());
|
||||
}
|
||||
observation.start();
|
||||
|
||||
this.scope = observation.openScope();
|
||||
}
|
||||
|
||||
private String getValueOrDefault(Object value) {
|
||||
return (value != null) ? value.toString() : UNKNOWN;
|
||||
}
|
||||
|
||||
public void onTaskFailed(Throwable throwable) {
|
||||
this.taskObservationContext.setStatus(STATUS_FAILURE);
|
||||
this.scope.getCurrentObservation().error(throwable);
|
||||
}
|
||||
|
||||
public void onTaskEnd(TaskExecution taskExecution) {
|
||||
if (this.scope != null) {
|
||||
this.taskObservationContext.getTaskExecution().setExitCode(taskExecution.getExitCode());
|
||||
this.scope.close();
|
||||
this.scope.getCurrentObservation().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.task.configuration.SingleTaskConfiguration,\
|
||||
org.springframework.cloud.task.configuration.SimpleTaskAutoConfiguration,\
|
||||
org.springframework.cloud.task.configuration.MetricsAutoConfiguration,\
|
||||
org.springframework.cloud.task.configuration.observation.ObservationTaskAutoConfiguration
|
||||
|
||||
|
||||
@@ -1,134 +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.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.simple.SimpleConfig;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(classes = { AbstractMicrometerTest.AutoConfigurationApplication.class })
|
||||
@DirtiesContext
|
||||
public class AbstractMicrometerTest {
|
||||
|
||||
@Autowired
|
||||
protected SimpleMeterRegistry simpleMeterRegistry;
|
||||
|
||||
@Autowired
|
||||
protected ConfigurableApplicationContext context;
|
||||
|
||||
protected Meter meter;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
Metrics.globalRegistry.getMeters().forEach(Metrics.globalRegistry::remove);
|
||||
assertThat(simpleMeterRegistry).isNotNull();
|
||||
meter = simpleMeterRegistry.find("spring.integration.handlers").meter();
|
||||
assertThat(meter).isNotNull().withFailMessage(
|
||||
"The spring.integration.handlers meter must be present in SpringBoot apps!");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
Metrics.globalRegistry.getMeters().forEach(Metrics.globalRegistry::remove);
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
String serviceJson = StreamUtils.copyToString(new DefaultResourceLoader()
|
||||
.getResource("classpath:/micrometer/pcf-scs-info.json").getInputStream(),
|
||||
Charset.forName("UTF-8"));
|
||||
mockVcapServicesFromString(serviceJson);
|
||||
}
|
||||
|
||||
public static MockUp<?> mockVcapServicesFromString(String serviceJson) {
|
||||
final Map<String, String> env = System.getenv();
|
||||
return new MockUp<System>() {
|
||||
@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<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Observation.Context> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<module>task-events</module>
|
||||
<module>batch-events</module>
|
||||
<module>jpa-sample</module>
|
||||
<module>task-metrics</module>
|
||||
<module>task-observations</module>
|
||||
<module>multiple-datasources</module>
|
||||
<module>single-step-batch-job</module>
|
||||
</modules>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.spring</groupId>
|
||||
<artifactId>task-metrics</artifactId>
|
||||
<artifactId>task-observations</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<name>task-metrics</name>
|
||||
<description>task-metrics</description>
|
||||
<name>task observation sample</name>
|
||||
<description>Displays task observations as well as commandline and application runner observations</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring-cloud.version>2022.0.0-SNAPSHOT</spring-cloud.version>
|
||||
@@ -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();
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
|
||||
@@ -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=");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,6 +48,7 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-task-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user