Fix repeating custom values in build scan. Refactor build statistic
This commit is contained in:
+3
-3
@@ -13,7 +13,7 @@ import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.jetbrains.kotlin.build.report.metrics.*
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity
|
||||
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_LEVEL
|
||||
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_MEMBER_LEVEL
|
||||
@@ -31,7 +31,7 @@ abstract class ClasspathEntrySnapshotTransform : TransformAction<ClasspathEntryS
|
||||
abstract val gradleUserHomeDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val buildMetricsReporterService: Property<BuildMetricsReporterService>
|
||||
abstract val buildMetricsService: Property<BuildMetricsService>
|
||||
}
|
||||
|
||||
@get:Classpath
|
||||
@@ -44,7 +44,7 @@ abstract class ClasspathEntrySnapshotTransform : TransformAction<ClasspathEntryS
|
||||
|
||||
val granularity = getClassSnapshotGranularity(classpathEntryInputDirOrJar, parameters.gradleUserHomeDir.get().asFile)
|
||||
|
||||
val buildMetricsReporterService = parameters.buildMetricsReporterService.orNull
|
||||
val buildMetricsReporterService = parameters.buildMetricsService.orNull
|
||||
val metricsReporter = buildMetricsReporterService?.let { BuildMetricsReporterImpl() } ?: DoNothingBuildMetricsReporter
|
||||
|
||||
val startTimeMs = System.currentTimeMillis()
|
||||
|
||||
+4
-7
@@ -41,8 +41,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.kpm.FragmentMappedKotlinSourceSetFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.HttpReportService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportsService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
|
||||
@@ -92,12 +92,9 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
|
||||
KotlinGradleBuildServices.registerIfAbsent(project).get()
|
||||
KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
|
||||
|
||||
BuildMetricsReporterService.registerIfAbsent(project)?.also {
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
|
||||
}
|
||||
|
||||
HttpReportService.registerIfAbsent(project, kotlinPluginVersion)?.also {
|
||||
BuildMetricsService.registerIfAbsent(project)?.also {
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
|
||||
BuildReportsService.registerIfAbsent(project, it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-50
@@ -20,27 +20,17 @@ import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
abstract class BuildMetricsReporterService : BuildService<BuildMetricsReporterService.Parameters>,
|
||||
OperationCompletionListener, AutoCloseable {
|
||||
|
||||
interface Parameters : BuildServiceParameters {
|
||||
var buildDataProcessors: List<BuildExecutionDataProcessor>
|
||||
var startParameters: GradleBuildStartParameters
|
||||
var reportingSettings: ReportingSettings
|
||||
}
|
||||
abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>, OperationCompletionListener {
|
||||
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
|
||||
// Tasks and transforms' records
|
||||
private val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>()
|
||||
private val failureMessages = ConcurrentLinkedQueue<String>()
|
||||
internal val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>()
|
||||
internal val failureMessages = ConcurrentLinkedQueue<String>()
|
||||
|
||||
// Info for tasks only
|
||||
private val taskPathToMetricsReporter = ConcurrentHashMap<String, BuildMetricsReporter>()
|
||||
@@ -102,55 +92,24 @@ abstract class BuildMetricsReporterService : BuildService<BuildMetricsReporterSe
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
val buildData = BuildExecutionData(
|
||||
startParameters = parameters.startParameters,
|
||||
failureMessages = failureMessages.toList(),
|
||||
buildOperationRecord = buildOperationRecords.sortedBy { it.startTimeMs }
|
||||
)
|
||||
parameters.buildDataProcessors.forEach { it.process(buildData, log) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serviceClass = BuildMetricsService::class.java
|
||||
private val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
|
||||
|
||||
fun getStartParameters(project: Project) = project.gradle.startParameter.let {
|
||||
GradleBuildStartParameters(
|
||||
tasks = it.taskRequests.flatMap { it.args },
|
||||
excludedTasks = it.excludedTaskNames,
|
||||
currentDir = it.currentDir.path,
|
||||
projectProperties = it.projectProperties.map {(key, value) -> "$key: $value"},
|
||||
systemProperties = it.systemPropertiesArgs.map {(key, value) -> "$key: $value"},
|
||||
)
|
||||
}
|
||||
|
||||
fun registerIfAbsent(project: Project): Provider<BuildMetricsReporterService>? {
|
||||
val serviceClass = BuildMetricsReporterService::class.java
|
||||
val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
|
||||
|
||||
fun registerIfAbsent(project: Project): Provider<BuildMetricsService>? {
|
||||
// Return early if the service was already registered to avoid the overhead of reading the reporting settings below
|
||||
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return it.service as Provider<BuildMetricsReporterService>
|
||||
return it.service as Provider<BuildMetricsService>
|
||||
}
|
||||
|
||||
//do not need to collect metrics if there aren't consumers for this data
|
||||
val reportingSettings = reportingSettings(project.rootProject)
|
||||
if (reportingSettings.buildReportOutputs.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
|
||||
val buildDataProcessors = mutableListOf<BuildExecutionDataProcessor>()
|
||||
reportingSettings.fileReportSettings?.let { fileReportSettings ->
|
||||
buildDataProcessors.add(PlainTextBuildReportWriterDataProcessor(fileReportSettings, project.rootProject.name))
|
||||
}
|
||||
reportingSettings.singleOutputFile?.let { singleOutputFile ->
|
||||
buildDataProcessors.add(MetricsWriter(singleOutputFile.absoluteFile))
|
||||
}
|
||||
|
||||
it.parameters.startParameters = getStartParameters(project)
|
||||
it.parameters.buildDataProcessors = buildDataProcessors
|
||||
it.parameters.reportingSettings = reportingSettings
|
||||
}!!
|
||||
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {}!!
|
||||
}
|
||||
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.gradle.scan.plugin.BuildScanExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.tooling.events.FinishEvent
|
||||
import org.gradle.tooling.events.OperationCompletionListener
|
||||
import org.gradle.tooling.events.task.TaskFinishEvent
|
||||
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
|
||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.BuildFinishStatisticsData
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildScanStatisticsListener
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatListener
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
abstract class BuildReportsService : BuildService<BuildReportsService.Parameters>, AutoCloseable, OperationCompletionListener {
|
||||
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
|
||||
private val startTime = System.nanoTime()
|
||||
private val buildUuid = UUID.randomUUID().toString()
|
||||
private var executorService: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
|
||||
|
||||
init {
|
||||
log.info("Build report service is registered. Unique build id: $buildUuid")
|
||||
}
|
||||
|
||||
interface Parameters : BuildServiceParameters {
|
||||
val startParameters: Property<GradleBuildStartParameters>
|
||||
val reportingSettings: Property<ReportingSettings>
|
||||
var buildMetricsService: Provider<BuildMetricsService>
|
||||
|
||||
val label: Property<String?>
|
||||
val projectName: Property<String>
|
||||
val kotlinVersion: Property<String>
|
||||
val additionalTags: ListProperty<StatTag>
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
val buildData = BuildExecutionData(
|
||||
startParameters = parameters.startParameters.get(),
|
||||
failureMessages = parameters.buildMetricsService.orNull?.failureMessages?.toList() ?: emptyList(),
|
||||
buildOperationRecord = parameters.buildMetricsService.orNull?.buildOperationRecords?.sortedBy { it.startTimeMs } ?: emptyList()
|
||||
)
|
||||
|
||||
val reportingSettings = parameters.reportingSettings.get()
|
||||
|
||||
reportingSettings.httpReportSettings?.also {
|
||||
executorService.submit { reportBuildFinish() } //
|
||||
}
|
||||
reportingSettings.fileReportSettings?.also {
|
||||
reportBuildStatInFile(it, buildData)
|
||||
}
|
||||
|
||||
reportingSettings.singleOutputFile?.also { singleOutputFile ->
|
||||
MetricsWriter(singleOutputFile.absoluteFile).process(buildData, log)
|
||||
}
|
||||
|
||||
//It's expected that bad internet connection can cause a significant delay for big project
|
||||
executorService.shutdownNow()
|
||||
}
|
||||
|
||||
override fun onFinish(event: FinishEvent?) {
|
||||
addHttpReport(event)
|
||||
}
|
||||
|
||||
private fun reportBuildStatInFile(fileReportSettings: FileReportSettings, buildData: BuildExecutionData) {
|
||||
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
|
||||
val reportFile = fileReportSettings.buildReportDir.resolve("${parameters.projectName.get()}-build-$ts.txt")
|
||||
|
||||
PlainTextBuildReportWriter(
|
||||
outputFile = reportFile,
|
||||
printMetrics = fileReportSettings.includeMetricsInReport
|
||||
).process(buildData, log)
|
||||
}
|
||||
|
||||
private fun reportBuildFinish() {
|
||||
val buildFinishData = BuildFinishStatisticsData(
|
||||
projectName = parameters.projectName.get(),
|
||||
startParameters = parameters.startParameters.get(),
|
||||
buildUuid = buildUuid,
|
||||
label = parameters.label.orNull,
|
||||
totalTime = (System.nanoTime() - startTime) / 1_000_000
|
||||
)
|
||||
sendDataViaHttp(buildFinishData)
|
||||
}
|
||||
|
||||
private fun initService(project: Project) {
|
||||
addListeners(project)
|
||||
}
|
||||
|
||||
private fun addBuildScanReport(project: Project) {
|
||||
if (parameters.reportingSettings.get().buildReportOutputs.contains(BuildReportType.BUILD_SCAN)) {
|
||||
val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
|
||||
project.rootProject.extensions.findByName("buildScan")
|
||||
?.also {
|
||||
listenerRegistryHolder.listenerRegistry.onTaskCompletion(
|
||||
project.provider {
|
||||
BuildScanStatisticsListener(
|
||||
it as BuildScanExtension,
|
||||
parameters.projectName.get(),
|
||||
parameters.label.orNull,
|
||||
parameters.kotlinVersion.get(),
|
||||
buildUuid
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHttpReport(event: FinishEvent?) {
|
||||
if (parameters.reportingSettings.get().httpReportSettings != null) {
|
||||
if (event is TaskFinishEvent) {
|
||||
val data =
|
||||
KotlinBuildStatListener.prepareData(
|
||||
event,
|
||||
parameters.projectName.get(),
|
||||
buildUuid,
|
||||
parameters.label.orNull,
|
||||
parameters.kotlinVersion.get(),
|
||||
parameters.additionalTags.get()
|
||||
)
|
||||
data?.also { executorService.submit { sendDataViaHttp(data) } }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun addListeners(project: Project) {
|
||||
addBuildScanReport(project)
|
||||
}
|
||||
|
||||
private var invalidUrl = false
|
||||
private var requestPreviousFailed = false
|
||||
private fun sendDataViaHttp(data: Any) {
|
||||
val httpSettings = parameters.reportingSettings.get().httpReportSettings ?: return
|
||||
|
||||
val elapsedTime = measureTimeMillis {
|
||||
if (invalidUrl) {
|
||||
return
|
||||
}
|
||||
val connection = try {
|
||||
URL(httpSettings.url).openConnection() as HttpURLConnection
|
||||
} catch (e: IOException) {
|
||||
log.warn("Unable to open connection to ${httpSettings.url}: ${e.message}")
|
||||
invalidUrl = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (httpSettings.user != null && httpSettings.password != null) {
|
||||
val auth = Base64.getEncoder()
|
||||
.encode("${httpSettings.user}:${httpSettings.password}".toByteArray())
|
||||
.toString(Charsets.UTF_8)
|
||||
connection.addRequestProperty("Authorization", "Basic $auth")
|
||||
}
|
||||
connection.addRequestProperty("Content-Type", "application/json")
|
||||
connection.requestMethod = "POST"
|
||||
connection.doOutput = true
|
||||
connection.outputStream.use {
|
||||
it.write(Gson().toJson(data).toByteArray())
|
||||
}
|
||||
connection.connect()
|
||||
checkResponseAndLog(connection)
|
||||
} catch (e: Exception) {
|
||||
log.debug("Unexpected exception happened ${e.message}: ${e.stackTrace}")
|
||||
checkResponseAndLog(connection)
|
||||
} finally {
|
||||
connection.disconnect()
|
||||
}
|
||||
}
|
||||
log.debug("Report statistic by http takes $elapsedTime ms")
|
||||
}
|
||||
|
||||
private fun checkResponseAndLog(connection: HttpURLConnection) {
|
||||
val isResponseBad = connection.responseCode !in 200..299
|
||||
if (isResponseBad) {
|
||||
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
|
||||
if (!requestPreviousFailed) {
|
||||
log.warn(message)
|
||||
} else {
|
||||
log.debug(message)
|
||||
}
|
||||
requestPreviousFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun getStartParameters(project: Project) = project.gradle.startParameter.let {
|
||||
GradleBuildStartParameters(
|
||||
tasks = it.taskRequests.flatMap { it.args },
|
||||
excludedTasks = it.excludedTaskNames,
|
||||
currentDir = it.currentDir.path,
|
||||
projectProperties = it.projectProperties.map { (key, value) -> "$key: $value" },
|
||||
systemProperties = it.systemPropertiesArgs.map { (key, value) -> "$key: $value" },
|
||||
)
|
||||
}
|
||||
|
||||
fun registerIfAbsent(project: Project, buildMetricsService: Provider<BuildMetricsService>): Provider<BuildReportsService>? {
|
||||
val serviceClass = BuildReportsService::class.java
|
||||
val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
|
||||
|
||||
val reportingSettings = reportingSettings(project.rootProject)
|
||||
if (reportingSettings.buildReportOutputs.isEmpty()) {
|
||||
return null //no need to collect data
|
||||
}
|
||||
|
||||
val kotlinVersion = project.getKotlinPluginVersion()
|
||||
val gradle = project.gradle
|
||||
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return it.service as Provider<BuildReportsService>
|
||||
}
|
||||
|
||||
return gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
|
||||
it.parameters.label.set(reportingSettings.buildReportLabel)
|
||||
it.parameters.projectName.set(project.rootProject.name)
|
||||
it.parameters.kotlinVersion.set(kotlinVersion)
|
||||
it.parameters.startParameters.set(getStartParameters(project))
|
||||
it.parameters.reportingSettings.set(reportingSettings)
|
||||
it.parameters.buildMetricsService = buildMetricsService
|
||||
|
||||
//init gradle tags for build scan and http reports
|
||||
it.parameters.additionalTags.value(setupTags(gradle))
|
||||
}!!.also {
|
||||
val buildReportsService = it.get()
|
||||
buildReportsService.initService(project)
|
||||
if (buildReportsService.parameters.reportingSettings.get().buildReportOutputs.contains(BuildReportType.HTTP)) {
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTags(gradle: Gradle): ArrayList<StatTag> {
|
||||
val additionalTags = ArrayList<StatTag>()
|
||||
if (isConfigurationCacheAvailable(gradle)) {
|
||||
additionalTags.add(StatTag.CONFIGURATION_CACHE)
|
||||
}
|
||||
if (gradle.startParameter.isBuildCacheEnabled) {
|
||||
additionalTags.add(StatTag.BUILD_CACHE)
|
||||
}
|
||||
return additionalTags
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.gradle.scan.plugin.BuildScanExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.tooling.events.FinishEvent
|
||||
import org.gradle.tooling.events.OperationCompletionListener
|
||||
import org.gradle.tooling.events.task.TaskFinishEvent
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.BuildFinishStatisticsData
|
||||
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters
|
||||
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildScanStatisticsListener
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatListener.Companion.prepareData
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService.Companion.getStartParameters
|
||||
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
abstract class HttpReportService : BuildService<HttpReportService.Parameters>,
|
||||
OperationCompletionListener, AutoCloseable {
|
||||
|
||||
var executorService: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
|
||||
val startTime = System.nanoTime()
|
||||
|
||||
interface Parameters : BuildServiceParameters {
|
||||
var label: String?
|
||||
var projectName: String
|
||||
var httpSettings: HttpReportSettings
|
||||
var kotlinVersion: String
|
||||
var additionalTags: List<StatTag>
|
||||
var startParameters: GradleBuildStartParameters
|
||||
}
|
||||
|
||||
val log = Logging.getLogger(this.javaClass)
|
||||
|
||||
init {
|
||||
log.info("Http report service is registered. Unique build id: $buildUuid")
|
||||
}
|
||||
|
||||
// @Volatile for one thread executor it does not need
|
||||
private var requestPreviousFailed = false
|
||||
private var invalidUrl = false
|
||||
|
||||
override fun onFinish(event: FinishEvent?) {
|
||||
if (event is TaskFinishEvent) {
|
||||
val data =
|
||||
prepareData(event, parameters.projectName, buildUuid, parameters.label, parameters.kotlinVersion, parameters.additionalTags)
|
||||
data?.also { executorService.submit { report(data) } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
executorService.submit { reportBuildFinish() }
|
||||
executorService.shutdown()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val buildUuid = UUID.randomUUID().toString()
|
||||
|
||||
fun registerIfAbsent(project: Project, kotlinVersion: String): Provider<HttpReportService>? {
|
||||
val gradle = project.gradle
|
||||
val rootProject = gradle.rootProject
|
||||
val reportingSettings = reportingSettings(rootProject)
|
||||
|
||||
addListeners(project, reportingSettings, kotlinVersion)
|
||||
|
||||
return reportingSettings.httpReportSettings?.let { httpSettings ->
|
||||
gradle.sharedServices.registerIfAbsent(
|
||||
"build_http_metric_service_${HttpReportService::class.java.classLoader.hashCode()}",
|
||||
HttpReportService::class.java
|
||||
) {
|
||||
it.parameters.label = reportingSettings.buildReportLabel
|
||||
it.parameters.projectName = rootProject.name
|
||||
it.parameters.httpSettings = httpSettings
|
||||
it.parameters.kotlinVersion = kotlinVersion
|
||||
it.parameters.startParameters = getStartParameters(project)
|
||||
|
||||
//init gradle tags, that present in build scan
|
||||
val additionalTags = ArrayList<StatTag>()
|
||||
if (isConfigurationCacheAvailable(gradle)) {
|
||||
additionalTags.add(StatTag.CONFIGURATION_CACHE)
|
||||
}
|
||||
if (gradle.startParameter.isBuildCacheEnabled) {
|
||||
additionalTags.add(StatTag.BUILD_CACHE)
|
||||
}
|
||||
it.parameters.additionalTags = additionalTags
|
||||
}!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun addListeners(project: Project, reportingSettings: ReportingSettings, kotlinVersion: String) {
|
||||
if (reportingSettings.buildReportOutputs.contains(BuildReportType.BUILD_SCAN)) {
|
||||
project.rootProject.extensions.findByName("buildScan")
|
||||
?.also {
|
||||
val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
|
||||
listenerRegistryHolder.listenerRegistry.onTaskCompletion(
|
||||
project.provider {
|
||||
BuildScanStatisticsListener(
|
||||
it as BuildScanExtension,
|
||||
project.rootProject.name,
|
||||
reportingSettings.buildReportLabel,
|
||||
kotlinVersion,
|
||||
buildUuid
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun report(data: Any) {
|
||||
val elapsedTime = measureTimeMillis {
|
||||
if (invalidUrl) {
|
||||
return
|
||||
}
|
||||
val connection = try {
|
||||
URL(parameters.httpSettings.url).openConnection() as HttpURLConnection
|
||||
} catch (e: IOException) {
|
||||
log.warn("Unable to open connection to ${parameters.httpSettings.url}: ${e.message}")
|
||||
invalidUrl = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (parameters.httpSettings.user != null && parameters.httpSettings.password != null) {
|
||||
val auth = Base64.getEncoder()
|
||||
.encode("${parameters.httpSettings.user}:${parameters.httpSettings.password}".toByteArray())
|
||||
.toString(Charsets.UTF_8)
|
||||
connection.addRequestProperty("Authorization", "Basic $auth")
|
||||
}
|
||||
connection.addRequestProperty("Content-Type", "application/json")
|
||||
connection.requestMethod = "POST"
|
||||
connection.doOutput = true
|
||||
connection.outputStream.use {
|
||||
it.write(Gson().toJson(data).toByteArray())
|
||||
}
|
||||
connection.connect()
|
||||
checkResponseAndLog(connection)
|
||||
connection.inputStream.use { it.reader().use { reader -> reader.readText() } }
|
||||
} catch (e: Exception) {
|
||||
log.debug("Unexpected exception happened ${e.message}: ${e.stackTrace}")
|
||||
checkResponseAndLog(connection)
|
||||
} finally {
|
||||
connection.disconnect()
|
||||
}
|
||||
}
|
||||
log.debug("Report statistic by http takes $elapsedTime ms")
|
||||
}
|
||||
|
||||
private fun reportBuildFinish() {
|
||||
val buildFinishData = BuildFinishStatisticsData(
|
||||
projectName = parameters.projectName,
|
||||
startParameters = parameters.startParameters,
|
||||
buildUuid = buildUuid,
|
||||
label = parameters.label,
|
||||
totalTime = (System.nanoTime() - startTime) / 1_000_000
|
||||
)
|
||||
report(buildFinishData)
|
||||
}
|
||||
|
||||
private fun checkResponseAndLog(connection: HttpURLConnection) {
|
||||
val isResponseBad = connection.responseCode !in 200..299
|
||||
if (isResponseBad) {
|
||||
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
|
||||
if (!requestPreviousFailed) {
|
||||
log.warn(message)
|
||||
} else {
|
||||
log.debug(message)
|
||||
}
|
||||
requestPreviousFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -12,15 +12,14 @@ import org.jetbrains.kotlin.gradle.internal.build.metrics.GradleBuildMetricsData
|
||||
import org.jetbrains.kotlin.gradle.internal.build.metrics.BuildOperationData
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import java.io.File
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.Serializable
|
||||
|
||||
internal class MetricsWriter(
|
||||
private val outputFile: File,
|
||||
) : BuildExecutionDataProcessor, Serializable {
|
||||
override fun process(build: BuildExecutionData, log: Logger) {
|
||||
): Serializable {
|
||||
fun process(build: BuildExecutionData, log: Logger) {
|
||||
if (build.failureMessages.isNotEmpty()) return
|
||||
|
||||
try {
|
||||
|
||||
-17
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.report
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.build.report.metrics.*
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
|
||||
import org.jetbrains.kotlin.gradle.utils.Printer
|
||||
import org.jetbrains.kotlin.gradle.utils.asString
|
||||
@@ -16,25 +15,9 @@ import org.jetbrains.kotlin.gradle.utils.formatSize
|
||||
import org.jetbrains.kotlin.gradle.utils.formatTime
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
|
||||
internal class PlainTextBuildReportWriterDataProcessor(
|
||||
val reportingSettings: FileReportSettings,
|
||||
val rootProjectName: String
|
||||
) : BuildExecutionDataProcessor, Serializable {
|
||||
override fun process(build: BuildExecutionData, log: Logger) {
|
||||
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
|
||||
val reportFile = reportingSettings.buildReportDir.resolve("${rootProjectName}-build-$ts.txt")
|
||||
|
||||
PlainTextBuildReportWriter(
|
||||
outputFile = reportFile,
|
||||
printMetrics = reportingSettings.includeMetricsInReport
|
||||
).process(build, log)
|
||||
}
|
||||
}
|
||||
|
||||
internal class PlainTextBuildReportWriter(
|
||||
private val outputFile: File,
|
||||
private val printMetrics: Boolean
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report.data
|
||||
|
||||
import org.gradle.api.logging.Logger
|
||||
|
||||
interface BuildExecutionDataProcessor {
|
||||
fun process(build: BuildExecutionData, log: Logger)
|
||||
}
|
||||
+3
-3
@@ -12,7 +12,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDceDsl
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
|
||||
@@ -211,8 +211,8 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
task.description = "build webpack ${mode.name.toLowerCase()} bundle"
|
||||
task._destinationDirectory = binary.distribution.directory
|
||||
|
||||
BuildMetricsReporterService.registerIfAbsent(project)?.let {
|
||||
task.buildMetricsReporterService.value(it)
|
||||
BuildMetricsService.registerIfAbsent(project)?.let {
|
||||
task.buildMetricsService.value(it)
|
||||
}
|
||||
|
||||
task.dependsOn(
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.executeTaskBaseName
|
||||
@@ -237,8 +237,8 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
|
||||
task.description = "build webpack ${type.name.toLowerCase()} bundle"
|
||||
task._destinationDirectory = distribution.directory
|
||||
|
||||
BuildMetricsReporterService.registerIfAbsent(project)?.let {
|
||||
task.buildMetricsReporterService.value(it)
|
||||
BuildMetricsService.registerIfAbsent(project)?.let {
|
||||
task.buildMetricsService.value(it)
|
||||
}
|
||||
|
||||
task.commonConfigure(
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl
|
||||
@@ -73,7 +73,7 @@ constructor(
|
||||
get() = injected
|
||||
|
||||
@get:Internal
|
||||
internal abstract val buildMetricsReporterService: Property<BuildMetricsReporterService?>
|
||||
internal abstract val buildMetricsService: Property<BuildMetricsService?>
|
||||
|
||||
@get:Internal
|
||||
val metrics: Property<BuildMetricsReporter> = project.objects
|
||||
@@ -330,7 +330,7 @@ constructor(
|
||||
buildMetrics.addMetric(BuildPerformanceMetric.BUNDLE_SIZE, it)
|
||||
}
|
||||
|
||||
buildMetricsReporterService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
||||
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -51,8 +51,9 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportsService
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
@@ -270,16 +271,19 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
|
||||
internal open fun isIncrementalCompilationEnabled(): Boolean =
|
||||
incremental
|
||||
|
||||
@get:Internal
|
||||
val startParameters = BuildMetricsReporterService.getStartParameters(project)
|
||||
|
||||
@get:Input
|
||||
abstract val ownModuleName: Property<String>
|
||||
|
||||
@get:Internal
|
||||
internal abstract val buildMetricsReporterService: Property<BuildMetricsReporterService?>
|
||||
internal abstract val buildMetricsService: Property<BuildMetricsService?>
|
||||
|
||||
internal fun reportingSettings() = buildMetricsReporterService.orNull?.parameters?.reportingSettings ?: ReportingSettings()
|
||||
@get:Internal
|
||||
internal abstract val buildReportsService: Property<BuildReportsService?>
|
||||
|
||||
@get:Internal
|
||||
val startParameters = BuildReportsService.getStartParameters(project)
|
||||
|
||||
internal fun reportingSettings() = buildReportsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings()
|
||||
|
||||
@get:Internal
|
||||
protected val multiModuleICSettings: MultiModuleICSettings
|
||||
@@ -403,7 +407,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
|
||||
executeImpl(inputChanges, outputsBackup)
|
||||
}
|
||||
|
||||
buildMetricsReporterService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
||||
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
||||
}
|
||||
|
||||
protected open fun skipCondition(): Boolean = sources.isEmpty
|
||||
|
||||
+7
-3
@@ -19,7 +19,8 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPro
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinOptions
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportsService
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME
|
||||
import org.jetbrains.kotlin.project.model.LanguageSettings
|
||||
@@ -58,10 +59,13 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
|
||||
.disallowChanges()
|
||||
|
||||
task.localStateDirectories.from(task.taskBuildLocalStateDirectory).disallowChanges()
|
||||
BuildMetricsReporterService.registerIfAbsent(project)?.let {
|
||||
task.buildMetricsReporterService.value(it)
|
||||
val buildMetricsService = BuildMetricsService.registerIfAbsent(project)?.also {
|
||||
task.buildMetricsService.value(it)
|
||||
}
|
||||
|
||||
buildMetricsService?.let { BuildReportsService.registerIfAbsent(project, it) }
|
||||
?.let { task.buildReportsService.value(it) }
|
||||
|
||||
propertiesProvider.kotlinDaemonJvmArgs?.let { kotlinDaemonJvmArgs ->
|
||||
task.kotlinDaemonJvmArguments.set(providers.provider {
|
||||
kotlinDaemonJvmArgs.split("\\s+".toRegex())
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
|
||||
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.project.model.LanguageSettings
|
||||
|
||||
@@ -111,18 +111,18 @@ internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotl
|
||||
}
|
||||
project.extensions.extraProperties[TRANSFORMS_REGISTERED] = true
|
||||
|
||||
val buildMetricsReporterService = BuildMetricsReporterService.registerIfAbsent(project)
|
||||
val buildMetricsService = BuildMetricsService.registerIfAbsent(project)
|
||||
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
|
||||
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, JAR_ARTIFACT_TYPE)
|
||||
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
|
||||
it.parameters.gradleUserHomeDir.set(project.gradle.gradleUserHomeDir)
|
||||
buildMetricsReporterService?.apply { it.parameters.buildMetricsReporterService.set(this) }
|
||||
buildMetricsService?.apply { it.parameters.buildMetricsService.set(this) }
|
||||
}
|
||||
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
|
||||
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, DIRECTORY_ARTIFACT_TYPE)
|
||||
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
|
||||
it.parameters.gradleUserHomeDir.set(project.gradle.gradleUserHomeDir)
|
||||
buildMetricsReporterService?.apply { it.parameters.buildMetricsReporterService.set(this) }
|
||||
buildMetricsService?.apply { it.parameters.buildMetricsService.set(this) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user