diff --git a/README.md b/README.md index 64eda829..9c7636f1 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ Talaiot will send to the InfluxDb server defined in the configuration the values | retentionPolicyConfiguration | retention policy which is used for writing points | | publishBuildMetrics | Publish build metrics of the publisher, true by default | | publishTaskMetrics | Publish tasks metrics of the publisher, true by default | +| tags | Collection of BuildMetrics used as tags | Included in: `com.cdsap.talaiot` and `com.cdsap.talaiot.plugin.influxdb` plugins. diff --git a/build.gradle.kts b/build.gradle.kts index e69de29b..900f3f0a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -0,0 +1,9 @@ +subprojects { + // Address https://github.com/gradle/gradle/issues/4823: Force parent project evaluation before sub-project evaluation for Kotlin build scripts + if (gradle.startParameter.isConfigureOnDemand + && buildscript.sourceFile?.extension?.toLowerCase() == "kts" + && parent != rootProject) { + generateSequence(parent) { project -> project.parent.takeIf { it != rootProject } } + .forEach { evaluationDependsOn(it.path) } + } +} \ No newline at end of file diff --git a/library/core/talaiot-test-utils/build.gradle.kts b/library/core/talaiot-test-utils/build.gradle.kts index 1facf5e7..9b7bbb35 100644 --- a/library/core/talaiot-test-utils/build.gradle.kts +++ b/library/core/talaiot-test-utils/build.gradle.kts @@ -11,7 +11,7 @@ talaiotLib { dependencies { implementation(project(":library:core:talaiot-logger")) implementation("org.testcontainers:testcontainers:1.11.3") - api("org.testcontainers:influxdb:1.11.3") + api("org.testcontainers:influxdb:1.15.2") api("org.testcontainers:elasticsearch:1.12.0") } diff --git a/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/BuildMetrics.kt b/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/BuildMetrics.kt new file mode 100644 index 00000000..bedc5e4d --- /dev/null +++ b/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/BuildMetrics.kt @@ -0,0 +1,58 @@ +package com.cdsap.talaiot.metrics + +enum class BuildMetrics { + Duration, + Configuration, + Success, + BuildId, + BuildInvocationId, + RequestedTasks, + CacheRatio, + Start, + RootProject, + OsVersion, + MaxWorkers, + JavaRuntime, + JavaVmName, + JavaXmsBytes, + JavaXmxBytes, + JavaMaxPermSize, + TotalRamAvailableBytes, + CpuCount, + Locale, + Username, + DefaultCharset, + IdeVersion, + GradleVersion, + GitBranch, + GitUser, + Hostname, + OsManufacturer, + PublicIp, + CacheUrl, + LocalCacheHit, + LocalCacheMiss, + RemoteCacheHit, + RemoteCacheMiss, + CacheStore, + SwitchCache, + SwitchScan, + SwitchConfigurationOnDemand, + SwitchContinueOnFailure, + SwitchDaemon, + SwitchDryRun, + SwitchOffline, + SwitchParallel, + SwitchRefreshDependencies, + SwitchRerunTasks, + Custom; + + override fun toString(): String { + return if (super.toString().startsWith("Switch")) { + val temp = super.toString().split("Switch") + "switch.${temp[1].decapitalize()}" + } else { + super.toString().decapitalize() + } + } +} diff --git a/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/DefaultBuildDataProvider.kt b/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/DefaultBuildDataProvider.kt index 5dde35fa..fe18dbd9 100644 --- a/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/DefaultBuildDataProvider.kt +++ b/library/core/talaiot/src/main/kotlin/com/cdsap/talaiot/metrics/DefaultBuildDataProvider.kt @@ -1,7 +1,7 @@ package com.cdsap.talaiot.metrics import com.cdsap.talaiot.entities.ExecutionReport - +import com.cdsap.talaiot.metrics.BuildMetrics.* class DefaultBuildMetricsProvider( private val report: ExecutionReport ) : ValuesProvider { @@ -9,54 +9,53 @@ class DefaultBuildMetricsProvider( override fun get(): Map { val map = mutableMapOf() - map["duration"] = report.durationMs?.toLong() ?: 0L - map["configuration"] = report.configurationDurationMs?.toLong() ?: 0L + map[Duration.toString()] = report.durationMs?.toLong() ?: 0L + map[Configuration.toString()] = report.configurationDurationMs?.toLong() ?: 0L with(report) { - success.let { map["success"] = it } - buildId?.let { map["buildId"] = it } - buildInvocationId?.let { map["buildInvocationId"] = it } - requestedTasks?.let { map["requestedTasks"] = it } - cacheRatio?.let { map["cacheRatio"] = it.toDouble() } - beginMs?.let { map["start"] = it.toDouble() } - rootProject?.let { map["rootProject"] = it } + success.let { map[Success.toString()] = it } + buildId?.let { map[BuildId.toString()] = it } + buildInvocationId?.let { map[BuildInvocationId.toString()] = it } + requestedTasks?.let { map[RequestedTasks.toString()] = it } + cacheRatio?.let { map[CacheRatio.toString()] = it.toDouble() } + beginMs?.let { map[Start.toString()] = it.toDouble() } + rootProject?.let { map[RootProject.toString()] = it } with(environment) { - osVersion?.let { map["osVersion"] = it } - maxWorkers?.let { map["maxWorkers"] = it.toInt() } - javaRuntime?.let { map["javaRuntime"] = it } - javaVmName?.let { map["javaVmName"] = it } - javaXmsBytes?.let { map["javaXmsBytes"] = it.toLong() } - javaXmxBytes?.let { map["javaXmxBytes"] = it.toLong() } - javaMaxPermSize?.let { map["javaMaxPermSize"] = it.toLong() } - totalRamAvailableBytes?.let { map["totalRamAvailableBytes"] = it.toLong() } - cpuCount?.let { map["cpuCount"] = it.toInt() } - locale?.let { map["locale"] = it } - username?.let { map["username"] = it } - publicIp?.let { map["publicIp"] = it } - defaultChartset?.let { map["defaultCharset"] = it } - ideVersion?.let { map["ideVersion"] = it } - gradleVersion?.let { map["gradleVersion"] = it } - gitBranch?.let { map["gitBranch"] = it } - gitUser?.let { map["gitUser"] = it } - hostname?.let { map["hostname"] = it } - osManufacturer?.let { map["osManufacturer"] = it } - publicIp?.let { map["publicIp"] = it } - cacheUrl?.let { map["cacheUrl"] = it } - localCacheHit?.let { map["localCacheHit"] = it.toString() } - localCacheMiss?.let { map["localCacheMiss"] = it.toString() } - remoteCacheHit?.let { map["remoteCacheHit"] = it.toString() } - remoteCacheMiss?.let { map["remoteCacheMiss"] = it.toString() } - cacheStore?.let { map["cacheStore"] = it } - switches.buildCache?.let { map["switch.cache"] = it } - switches.buildScan?.let { map["switch.scan"] = it } - switches.configurationOnDemand?.let { map["switch.configurationOnDemand"] = it } - switches.continueOnFailure?.let { map["switch.continueOnFailure"] = it } - switches.daemon?.let { map["switch.daemon"] = it } - switches.dryRun?.let { map["switch.dryRun"] = it } - switches.offline?.let { map["switch.offline"] = it } - switches.parallel?.let { map["switch.parallel"] = it } - switches.refreshDependencies?.let { map["switch.refreshDependencies"] = it } - switches.rerunTasks?.let { map["switch.rerunTasks"] = it } + osVersion?.let { map[OsVersion.toString()] = it } + maxWorkers?.let { map[MaxWorkers.toString()] = it.toInt() } + javaRuntime?.let { map[JavaRuntime.toString()] = it } + javaVmName?.let { map[JavaVmName.toString()] = it } + javaXmsBytes?.let { map[JavaXmsBytes.toString()] = it.toLong() } + javaXmxBytes?.let { map[JavaXmxBytes.toString()] = it.toLong() } + javaMaxPermSize?.let { map[JavaMaxPermSize.toString()] = it.toLong() } + totalRamAvailableBytes?.let { map[TotalRamAvailableBytes.toString()] = it.toLong() } + cpuCount?.let { map[CpuCount.toString()] = it.toInt() } + locale?.let { map[Locale.toString()] = it } + username?.let { map[Username.toString()] = it } + defaultChartset?.let { map[DefaultCharset.toString()] = it } + ideVersion?.let { map[IdeVersion.toString()] = it } + gradleVersion?.let { map[GradleVersion.toString()] = it } + gitBranch?.let { map[GitBranch.toString()] = it } + gitUser?.let { map[GitUser.toString()] = it } + hostname?.let { map[Hostname.toString()] = it } + osManufacturer?.let { map[OsManufacturer.toString()] = it } + publicIp?.let { map[PublicIp.toString()] = it } + cacheUrl?.let { map[CacheUrl.toString()] = it } + localCacheHit?.let { map[LocalCacheHit.toString()] = it.toString() } + localCacheMiss?.let { map[LocalCacheMiss.toString()] = it.toString() } + remoteCacheHit?.let { map[RemoteCacheHit.toString()] = it.toString() } + remoteCacheMiss?.let { map[RemoteCacheMiss.toString()] = it.toString() } + cacheStore?.let { map[CacheStore.toString()] = it } + switches.buildCache?.let { map[SwitchCache.toString()] = it } + switches.buildScan?.let { map[SwitchScan.toString()] = it } + switches.configurationOnDemand?.let { map[SwitchConfigurationOnDemand.toString()] = it } + switches.continueOnFailure?.let { map[SwitchContinueOnFailure.toString()] = it } + switches.daemon?.let { map[SwitchDaemon.toString()] = it } + switches.dryRun?.let { map[SwitchDryRun.toString()] = it } + switches.offline?.let { map[SwitchOffline.toString()] = it } + switches.parallel?.let { map[SwitchParallel.toString()] = it } + switches.refreshDependencies?.let { map[SwitchRefreshDependencies.toString()] = it } + switches.rerunTasks?.let { map[SwitchRerunTasks.toString()] = it } } } map.putAll(report.customProperties.buildProperties) diff --git a/library/plugins/influxdb/influxdb-publisher/build.gradle.kts b/library/plugins/influxdb/influxdb-publisher/build.gradle.kts index c9dd8519..8e90b8ae 100644 --- a/library/plugins/influxdb/influxdb-publisher/build.gradle.kts +++ b/library/plugins/influxdb/influxdb-publisher/build.gradle.kts @@ -10,6 +10,6 @@ talaiotLib { dependencies { implementation(project(":library:core:talaiot")) - implementation("org.influxdb:influxdb-java:2.19") + implementation("org.influxdb:influxdb-java:2.21") testImplementation(project(":library:core:talaiot-test-utils")) } diff --git a/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisher.kt b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisher.kt index 6399dea9..7e324131 100644 --- a/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisher.kt +++ b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisher.kt @@ -2,7 +2,6 @@ package com.cdsap.talaiot.publisher.influxdb import com.cdsap.talaiot.entities.ExecutionReport import com.cdsap.talaiot.logger.LogTracker -import com.cdsap.talaiot.metrics.DefaultBuildMetricsProvider import com.cdsap.talaiot.metrics.DefaultTaskDataProvider import com.cdsap.talaiot.publisher.Publisher import okhttp3.OkHttpClient @@ -125,18 +124,19 @@ class InfluxDbPublisher( } private fun createBuildPoint(report: ExecutionReport): Point { - val metricsProvider = DefaultBuildMetricsProvider(report) + val tagFieldProvider = TagFieldProvider(report,influxDbPublisherConfiguration.tags) return Point.measurement(influxDbPublisherConfiguration.buildMetricName) - .time(report.endMs?.toLong() ?: System.currentTimeMillis(), TimeUnit.MILLISECONDS) - .fields(metricsProvider.get()) - .build() + .time(report.endMs?.toLong() ?: System.currentTimeMillis(), TimeUnit.MILLISECONDS) + .tag(tagFieldProvider.tags()) + .fields(tagFieldProvider.fields()) + .build() } private fun createDb(): InfluxDB { val okHttpBuilder = OkHttpClient.Builder() - .connectTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) + .connectTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) + .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) + .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) val user = influxDbPublisherConfiguration.username val password = influxDbPublisherConfiguration.password val url = influxDbPublisherConfiguration.url diff --git a/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherConfiguration.kt b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherConfiguration.kt index c02e16d0..0bd16688 100644 --- a/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherConfiguration.kt +++ b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherConfiguration.kt @@ -1,6 +1,7 @@ package com.cdsap.talaiot.publisher.influxdb import com.cdsap.talaiot.configuration.PublisherConfiguration +import com.cdsap.talaiot.metrics.BuildMetrics import groovy.lang.Closure /** @@ -52,6 +53,11 @@ class InfluxDbPublisherConfiguration : PublisherConfiguration { */ var retentionPolicyConfiguration: RetentionPolicyConfiguration = RetentionPolicyConfiguration() + /** + * by default all build metrics are considered fields, specify required [BuildMetrics] to be consider as Tags in InfluxDb + */ + var tags : List = emptyList() + /** * Configuration accessor within the [InfluxDbPublisherConfiguration] for the [com.cdsap.talaiot.configuration.RetentionPolicyConfiguration] * diff --git a/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProvider.kt b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProvider.kt new file mode 100644 index 00000000..d25cc706 --- /dev/null +++ b/library/plugins/influxdb/influxdb-publisher/src/main/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProvider.kt @@ -0,0 +1,37 @@ +package com.cdsap.talaiot.publisher.influxdb + +import com.cdsap.talaiot.entities.CustomProperties +import com.cdsap.talaiot.entities.Environment +import com.cdsap.talaiot.entities.ExecutionReport +import com.cdsap.talaiot.metrics.BuildMetrics +import com.cdsap.talaiot.metrics.DefaultBuildMetricsProvider +import java.lang.IllegalArgumentException + +class TagFieldProvider( + private val executionReport: ExecutionReport, + private val tagsConfiguration: List +) { + private val buildMetrics = DefaultBuildMetricsProvider(executionReport).get() + + fun tags(): Map = buildMetrics + .filter { + customMetrics(it.key) || defaultMetrics(it.key) + }.mapValues { it.value.toString() } + + + fun fields(): Map { + val tags = tags() + return buildMetrics.filter { !tags.containsKey(it.key) } + } + + private fun customMetrics(key: String) = + tagsConfiguration.contains(BuildMetrics.Custom) && + executionReport.customProperties.buildProperties.contains(key) + + private fun defaultMetrics(key: String) = + try { + tagsConfiguration.contains(BuildMetrics.valueOf(key.capitalize())) + } catch (e: IllegalArgumentException){ + false + } +} diff --git a/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbConfigurationTest.kt b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbConfigurationTest.kt index 1bfc1c2c..ea52213a 100644 --- a/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbConfigurationTest.kt +++ b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbConfigurationTest.kt @@ -1,6 +1,5 @@ package com.cdsap.talaiot.publisher.influxdb -import com.cdsap.talaiot.publisher.influxdb.InfluxDbPublisherConfiguration import io.kotlintest.specs.BehaviorSpec class InfluxDbConfigurationTest : BehaviorSpec({ @@ -10,13 +9,15 @@ class InfluxDbConfigurationTest : BehaviorSpec({ val influxDbPublisherConfiguration = InfluxDbPublisherConfiguration() then("default values are given") { assert( - influxDbPublisherConfiguration.retentionPolicyConfiguration.name == "rpTalaiot" - && influxDbPublisherConfiguration.retentionPolicyConfiguration.duration == "30d" + influxDbPublisherConfiguration.retentionPolicyConfiguration.name == "rpTalaiot" && + influxDbPublisherConfiguration.retentionPolicyConfiguration.duration == "30d" && + influxDbPublisherConfiguration.tags.isEmpty() ) } } `when`("There is custom retention policy defined") { val influxDbPublisherConfiguration = InfluxDbPublisherConfiguration() + influxDbPublisherConfiguration.retentionPolicyConfiguration { name = "customRp" duration = "99w" diff --git a/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherTest.kt b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherTest.kt index cf0bbd50..1cd0a300 100644 --- a/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherTest.kt +++ b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/InfluxDbPublisherTest.kt @@ -3,6 +3,7 @@ package com.cdsap.talaiot.publisher.influxdb import com.cdsap.talaiot.entities.* import com.cdsap.talaiot.utils.TestExecutor import com.cdsap.talaiot.logger.TestLogTrackerRecorder +import com.cdsap.talaiot.metrics.BuildMetrics import io.kotlintest.Spec import io.kotlintest.specs.BehaviorSpec import org.influxdb.dto.Query @@ -183,7 +184,33 @@ class InfluxDbPublisherTest : BehaviorSpec() { } } + `when`("custom metrics are included as tags") { + val databaseTags = "databaseTags" + val influxDbConfiguration = InfluxDbPublisherConfiguration().apply { + dbName = databaseTags + url = container.url + taskMetricName = "task" + buildMetricName = "build" + publishTaskMetrics = false + tags = listOf(BuildMetrics.Custom, BuildMetrics.MaxWorkers) + } + val influxDbPublisher = InfluxDbPublisher( + influxDbConfiguration, logger, TestExecutor() + ) + + then("build metrics are sent and task metrics doesn't") { + influxDbPublisher.publish(executionReport()) + val buildResult = + influxDB.query(Query("select * from $databaseTags.rpTalaiot.build group by *")) + assert(buildResult.results[0].series[0].tags.containsKey(BuildMetrics.MaxWorkers.toString())) + assert(buildResult.results[0].series[0].tags.containsKey("metric3")) + assert(buildResult.results[0].series[0].tags.containsKey("metric4")) + assert(!buildResult.results[0].series[0].columns.contains(BuildMetrics.MaxWorkers.toString())) + assert(!buildResult.results[0].series[0].columns.contains("metric3")) + assert(!buildResult.results[0].series[0].columns.contains("metric4")) + } + } } } diff --git a/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProviderTest.kt b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProviderTest.kt new file mode 100644 index 00000000..a53faa95 --- /dev/null +++ b/library/plugins/influxdb/influxdb-publisher/src/test/kotlin/com/cdsap/talaiot/publisher/influxdb/TagFieldProviderTest.kt @@ -0,0 +1,95 @@ +package com.cdsap.talaiot.publisher.influxdb + +import com.cdsap.talaiot.entities.* +import com.cdsap.talaiot.utils.TestExecutor +import com.cdsap.talaiot.logger.TestLogTrackerRecorder +import com.cdsap.talaiot.metrics.BuildMetrics +import io.kotlintest.Spec +import io.kotlintest.specs.BehaviorSpec +import org.influxdb.dto.Query +import org.testcontainers.influxdb.KInfluxDBContainer + +class TagFieldProviderTest : BehaviorSpec() { + + init { + given("TagFieldProvider Instance") { + + `when`("No tags included in the configuration") { + val metrics = mutableMapOf("Metrics1" to "value1", "Metrics2" to "value2") + val customProperties = CustomProperties() + customProperties.buildProperties = metrics + + val executionReport = ExecutionReport( + customProperties = customProperties, + environment = Environment( + cpuCount = "12" + ) + ) + + val tagFieldProvider = TagFieldProvider( + executionReport, + emptyList() + ) + + then("tags are empty and fields contain CpuCount build metrics + 2 custom properties") { + assert(tagFieldProvider.tags().isEmpty()) + assert(tagFieldProvider.fields().containsKey(BuildMetrics.CpuCount.toString())) + assert(tagFieldProvider.fields().containsKey("Metrics1")) + assert(tagFieldProvider.fields().containsKey("Metrics2")) + } + } + + `when`("tag CpuCount included in the configuration") { + val metrics = mutableMapOf("Metrics1" to "value1", "Metrics2" to "value2") + val customProperties = CustomProperties() + customProperties.buildProperties = metrics + + val executionReport = ExecutionReport( + customProperties = customProperties, + environment = Environment( + cpuCount = "12" + ) + ) + + val tagFieldProvider = TagFieldProvider( + executionReport, + listOf(BuildMetrics.CpuCount) + ) + + then("tags includes CpuCount") { + assert(tagFieldProvider.tags().size == 1) + assert(tagFieldProvider.tags().containsKey(BuildMetrics.CpuCount.toString())) + assert(!tagFieldProvider.fields().containsKey(BuildMetrics.CpuCount.toString())) + } + } + + `when`("tags include Custom Metrics") { + val metrics = mutableMapOf("Metrics1" to "value1", "Metrics2" to "value2") + val customProperties = CustomProperties() + customProperties.buildProperties = metrics + + val executionReport = ExecutionReport( + customProperties = customProperties, + environment = Environment( + cpuCount = "12" + ) + ) + + val tagFieldProvider = TagFieldProvider( + executionReport, + listOf(BuildMetrics.Custom) + ) + + then("tags includes CpuCount") { + assert(tagFieldProvider.tags().size == 2) + assert(tagFieldProvider.tags().containsKey("Metrics1")) + assert(tagFieldProvider.tags().containsKey("Metrics2")) + assert(!tagFieldProvider.tags().containsKey(BuildMetrics.CpuCount.toString())) + assert(!tagFieldProvider.fields().containsKey("Metrics1")) + assert(!tagFieldProvider.fields().containsKey("Metrics2")) + assert(tagFieldProvider.fields().containsKey(BuildMetrics.CpuCount.toString())) + } + } + } + } +}