
ru.tinkoff.plugins.buildmetrics.gradle.metrics.internal.BuildDurationMetricsFactory.kt Maven / Gradle / Ivy
package ru.tinkoff.plugins.buildmetrics.gradle.metrics.internal
import org.gradle.api.invocation.BuildInvocationDetails
import org.gradle.execution.RunRootBuildWorkBuildOperationType
import org.gradle.initialization.ConfigureBuildBuildOperationType
import org.gradle.initialization.LoadBuildBuildOperationType
import org.gradle.internal.configurationcache.ConfigurationCacheLoadBuildOperationType
import org.gradle.internal.configurationcache.ConfigurationCacheStoreBuildOperationType
import org.gradle.internal.operations.BuildOperationCategory
import org.gradle.internal.taskgraph.CalculateTreeTaskGraphBuildOperationType
import ru.tinkoff.plugins.buildmetrics.api.builds.internal.BuildOperationData
import ru.tinkoff.plugins.buildmetrics.api.builds.internal.BuildOperationDataListener
import ru.tinkoff.plugins.buildmetrics.api.factories.Factory
import ru.tinkoff.plugins.buildmetrics.api.labels.Label
import ru.tinkoff.plugins.buildmetrics.api.metrics.Metric
import ru.tinkoff.plugins.buildmetrics.utils.system.SystemTime
import ru.tinkoff.plugins.buildmetrics.utils.system.SystemTimeImpl
import kotlin.math.max
import kotlin.math.min
/**
* Metrics:
* - gradle_build_duration_ms;
* - gradle_build_started_time_ms - label;
* - gradle_build_prepare_duration_ms - initializations & configurations phases duration in ms;
* - gradle_phase_initialization_duration_ms - initialization phase duration in ms;
* - gradle_phase_configuration_duration_ms - configuration phase duration in ms;
* - gradle_phase_execution_duration_ms - execution phase duration in ms. See [build_lifecycle](https://docs.gradle.org/current/userguide/build_lifecycle.html);
*/
class BuildDurationMetricsFactory(
buildDetails: BuildInvocationDetails,
private val systemTime: SystemTime = SystemTimeImpl(),
) : Factory.Metrics, BuildOperationDataListener {
private class FactoryData(buildStartedTime: Long) {
private var buildStartTime: Long = buildStartedTime
private var configurationStartTime: Long = Long.MAX_VALUE
private var configurationEndTime: Long = Long.MIN_VALUE
private var executionStartTime: Long = Long.MAX_VALUE
private var executionEndTime: Long = Long.MIN_VALUE
class BuildData(
val buildStartTime: Long,
val configurationStartTime: Long?,
val configurationEndTime: Long?,
val executionStartTime: Long?,
val executionEndTime: Long?,
)
fun onBuildOperationData(data: BuildOperationData) {
updateBuildStartTime(data = data)
updateConfigurationTime(data = data)
updateExecutionTime(data = data)
}
private fun updateBuildStartTime(data: BuildOperationData) {
val details = data.details
buildStartTime = if (details is RunRootBuildWorkBuildOperationType.Details) {
details.buildStartTime
} else {
min(buildStartTime, data.startTime)
}
}
private fun updateConfigurationTime(data: BuildOperationData) {
when (data.details) {
is LoadBuildBuildOperationType.Details,
is ConfigureBuildBuildOperationType.Details,
is CalculateTreeTaskGraphBuildOperationType.Details,
is ConfigurationCacheStoreBuildOperationType.Details,
is ConfigurationCacheLoadBuildOperationType.Details -> {
configurationStartTime = min(configurationStartTime, data.startTime)
configurationEndTime = max(configurationEndTime, data.endTime)
}
}
}
private fun updateExecutionTime(data: BuildOperationData) {
if (data.metadata == BuildOperationCategory.RUN_MAIN_TASKS) {
executionStartTime = min(executionStartTime, data.startTime)
executionEndTime = max(executionEndTime, data.endTime)
}
}
fun buildData(): BuildData = BuildData(
buildStartTime = buildStartTime,
configurationStartTime = configurationStartTime.nullIfNotAvailable(),
configurationEndTime = configurationEndTime.nullIfNotAvailable(),
executionStartTime = executionStartTime.nullIfNotAvailable(),
executionEndTime = executionEndTime.nullIfNotAvailable(),
)
private fun Long.nullIfNotAvailable(): Long? = takeIf { value ->
value > Long.MIN_VALUE && value < Long.MAX_VALUE
}
}
@Transient
private var factoryData: FactoryData = FactoryData(buildStartedTime = buildDetails.buildStartedTime)
override fun reinitialize() {
factoryData = FactoryData(buildStartedTime = systemTime.currentTimeMillis())
}
override fun onBuildOperationData(data: BuildOperationData) {
factoryData.onBuildOperationData(data = data)
}
override fun create(): List> {
val buildData = factoryData.buildData()
val buildEndTime = systemTime.currentTimeMillis()
return listOf(
buildDurationMetric(buildData = buildData, buildEndTime = buildEndTime),
buildPrepareDurationMetric(buildData = buildData),
initializationDurationMetric(buildData = buildData),
configurationDurationMetric(buildData = buildData),
executionDurationMetric(buildData = buildData),
)
}
private fun buildDurationMetric(
buildData: FactoryData.BuildData,
buildEndTime: Long,
): Metric = Metric(
name = "gradle_build_duration_ms",
value = buildEndTime - buildData.buildStartTime,
labels = listOf(
Label(name = "gradle_build_started_time_ms", value = buildData.buildStartTime),
)
)
private fun initializationDurationMetric(buildData: FactoryData.BuildData): Metric {
val initializationEndTime = buildData.configurationStartTime ?: buildData.executionStartTime
return Metric(
name = "gradle_phase_initialization_duration_ms",
value = if (initializationEndTime != null) {
initializationEndTime - buildData.buildStartTime
} else {
0L
},
)
}
private fun configurationDurationMetric(buildData: FactoryData.BuildData): Metric {
val configurationStartTime = buildData.configurationStartTime
val configurationEndTime = buildData.executionStartTime ?: buildData.configurationEndTime
return Metric(
name = "gradle_phase_configuration_duration_ms",
value = if (configurationStartTime != null && configurationEndTime != null) {
configurationEndTime - configurationStartTime
} else {
0L
},
)
}
private fun executionDurationMetric(buildData: FactoryData.BuildData): Metric {
val executionStartTime = buildData.executionStartTime
val executionEndTime = buildData.executionEndTime
return Metric(
name = "gradle_phase_execution_duration_ms",
value = if (executionStartTime != null && executionEndTime != null) {
executionEndTime - executionStartTime
} else {
0L
},
)
}
private fun buildPrepareDurationMetric(buildData: FactoryData.BuildData): Metric {
val configurationEndTime = buildData.executionStartTime ?: buildData.configurationEndTime
return Metric(
name = "gradle_build_prepare_duration_ms",
value = if (configurationEndTime != null) {
configurationEndTime - buildData.buildStartTime
} else {
0L
}
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy