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

ru.tinkoff.plugins.buildmetrics.internal.BuildMetricsService.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC2
Show newest version
package ru.tinkoff.plugins.buildmetrics.internal

import org.gradle.api.flow.BuildWorkResult
import org.gradle.api.provider.ListProperty
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.internal.operations.BuildOperationDescriptor
import org.gradle.internal.operations.BuildOperationListener
import org.gradle.internal.operations.OperationFinishEvent
import org.gradle.internal.operations.OperationIdentifier
import org.gradle.internal.operations.OperationProgressEvent
import org.gradle.internal.operations.OperationStartEvent
import ru.tinkoff.plugins.buildmetrics.api.Reinitializable
import ru.tinkoff.plugins.buildmetrics.api.builds.BuildWorkResultListener
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.handlers.BuildHandler
import ru.tinkoff.plugins.buildmetrics.api.labels.Label
import ru.tinkoff.plugins.buildmetrics.api.metrics.Metric
import ru.tinkoff.plugins.buildmetrics.api.publishers.Publisher

abstract class BuildMetricsService : BuildService,
    BuildOperationListener,
    AutoCloseable {

    interface Params : BuildServiceParameters {

        val metrics: ListProperty>

        val labels: ListProperty>

        val metricsFactories: ListProperty

        val labelsFactories: ListProperty

        val publishers: ListProperty

        val buildHandlers: ListProperty
    }

    private val locker: Any = Any()

    private val buildOperationsDataBuffer: MutableList = mutableListOf()

    private class ServiceData(
        metrics: ListProperty>,
        labels: ListProperty>,
        metricsFactories: ListProperty,
        labelsFactories: ListProperty,
        publishers: ListProperty,
        buildHandlers: ListProperty,
    ) {

        val metrics: List> = metrics.get()

        val labels: List> = labels.get()

        val metricsFactories: List = metricsFactories.get()

        val labelsFactories: List = labelsFactories.get()

        val publishers: List = publishers.get()

        val buildHandlers: List = buildHandlers.get()

        val buildOperationDataListeners: List

        val buildWorkResultListeners: List

        val buildOperationsDataBufferEnabled: Boolean

        init {
            this.metricsFactories.plus(this.labelsFactories)
                .plus(this.publishers)
                .plus(this.buildHandlers)
                .also { items ->
                    buildOperationDataListeners = items.filterIsInstance()
                    buildWorkResultListeners = items.filterIsInstance()
                }
            buildOperationsDataBufferEnabled = buildOperationDataListeners.isNotEmpty()
        }

        fun reinitialize() {
            metricsFactories.plus(labelsFactories)
                .plus(publishers)
                .plus(buildHandlers)
                .forEach { item ->
                    if (item is Reinitializable) {
                        item.reinitialize()
                    }
                }
        }
    }

    private var serviceData: ServiceData = ServiceData(
        metrics = parameters.metrics,
        labels = parameters.labels,
        metricsFactories = parameters.metricsFactories,
        labelsFactories = parameters.labelsFactories,
        publishers = parameters.publishers,
        buildHandlers = parameters.buildHandlers,
    ).apply {
        reinitialize()
    }

    fun setup(
        metrics: ListProperty>,
        labels: ListProperty>,
        metricsFactories: ListProperty,
        labelsFactories: ListProperty,
        publishers: ListProperty,
        buildHandlers: ListProperty
    ) {
        synchronized(locker) {
            serviceData = ServiceData(
                metrics = metrics,
                labels = labels,
                metricsFactories = metricsFactories,
                labelsFactories = labelsFactories,
                publishers = publishers,
                buildHandlers = buildHandlers,
            )
        }
    }

    override fun started(buildOperation: BuildOperationDescriptor, startEvent: OperationStartEvent) {}

    override fun progress(operationIdentifier: OperationIdentifier, progressEvent: OperationProgressEvent) {}

    override fun finished(buildOperation: BuildOperationDescriptor, finishEvent: OperationFinishEvent) {
        onBuildOperationData(
            data = BuildOperationData(
                id = buildOperation.id?.id ?: -1L,
                parentId = buildOperation.parentId?.id ?: -1L,
                startTime = finishEvent.startTime,
                endTime = finishEvent.endTime,
                durationMs = finishEvent.endTime - finishEvent.startTime,
                name = buildOperation.name,
                metadata = buildOperation.metadata,
                details = buildOperation.details,
                result = finishEvent.result,
                failure = finishEvent.failure,
            )
        )
    }

    private fun onBuildOperationData(data: BuildOperationData) {
        synchronized(locker) {
            if (serviceData.buildOperationDataListeners.isEmpty()) {
                if (serviceData.buildOperationsDataBufferEnabled) {
                    buildOperationsDataBuffer.add(data)
                }
            } else {
                if (buildOperationsDataBuffer.isNotEmpty()) {
                    flushBuildOperationsDataBuffer()
                }
                serviceData.buildOperationDataListeners.forEach { listener ->
                    listener.onBuildOperationData(data = data)
                }
            }
        }
    }

    fun onBuildWorkResult(buildWorkResult: BuildWorkResult) {
        synchronized(locker) {
            serviceData.buildWorkResultListeners.forEach { listener ->
                listener.onBuildWorkResult(result = buildWorkResult)
            }
        }
    }

    private fun flushBuildOperationsDataBuffer() {
        buildOperationsDataBuffer.forEach { bufferedData ->
            serviceData.buildOperationDataListeners.forEach { listener ->
                listener.onBuildOperationData(data = bufferedData)
            }
        }
        buildOperationsDataBuffer.clear()
    }

    override fun close() {
        synchronized(locker) {
            if (buildOperationsDataBuffer.isNotEmpty()) {
                flushBuildOperationsDataBuffer()
            }
            val metrics = createMetrics(labels = createLabels())
            serviceData.publishers.forEach { publisher -> publisher.publish(metrics = metrics) }
            serviceData.buildHandlers.forEach { handler -> handler.handle() }
        }
    }

    private fun createLabels(): List> {
        return serviceData.labelsFactories.flatMap { factory -> factory.create() }
            .plus(serviceData.labels)
    }

    private fun createMetrics(labels: List>): List> {
        return serviceData.metricsFactories.flatMap { factory -> factory.create() }
            .plus(serviceData.metrics)
            .map { metric ->
                Metric(
                    name = metric.name,
                    value = metric.value,
                    labels = metric.labels + labels,
                )
            }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy