All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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