Support weight for FUS metrics

If metric impact is different for different subprojects, corresponding
metric could be reported with weight. Currently supported for numerical
metrics with override policy AVERAGE.
This commit is contained in:
Andrey Uskov
2022-12-15 22:28:06 +03:00
parent f07f193938
commit 5e06c52073
7 changed files with 129 additions and 84 deletions
@@ -191,22 +191,33 @@ class KotlinBuildStatHandler {
}
}
internal fun report(sessionLogger: BuildSessionLogger, metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
internal fun report(
sessionLogger: BuildSessionLogger,
metric: BooleanMetrics,
value: Boolean,
subprojectName: String?,
weight: Long? = null
) = runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName, weight)
} ?: false
}
internal fun report(
sessionLogger: BuildSessionLogger,
metric: NumericalMetrics,
value: Long,
subprojectName: String?,
weight: Long? = null
) = runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName, weight)
} as? Boolean ?: false
internal fun report(sessionLogger: BuildSessionLogger, metric: NumericalMetrics, value: Long, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
}
internal fun report(sessionLogger: BuildSessionLogger, metric: StringMetrics, value: String, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
}
internal fun report(
sessionLogger: BuildSessionLogger,
metric: StringMetrics,
value: String,
subprojectName: String?,
weight: Long? = null
) = runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName, weight)
} as? Boolean ?: false
}
@@ -33,11 +33,11 @@ import kotlin.system.measureTimeMillis
* of Kotlin Plugin and other classloaders
*/
interface KotlinBuildStatsMXBean {
fun reportBoolean(name: String, value: Boolean, subprojectName: String?)
fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean
fun reportNumber(name: String, value: Long, subprojectName: String?)
fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean
fun reportString(name: String, value: String, subprojectName: String?)
fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean
}
@@ -158,32 +158,29 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) :
KotlinBuildStatsService() {
private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?) {
mbs.invoke(
private fun callJmx(method: String, type: String, metricName: String, value: Any, subprojectName: String?, weight: Long?): Any? {
return mbs.invoke(
beanName,
method,
arrayOf(metricName, value, subprojectName),
arrayOf("java.lang.String", type, "java.lang.String")
arrayOf(metricName, value, subprojectName, weight),
arrayOf("java.lang.String", type, "java.lang.String", "java.lang.Long")
)
}
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?) =
runSafe("report metric ${metric.name}") {
callJmx("reportBoolean", "boolean", metric.name, value, subprojectName)
}
}
callJmx("reportBoolean", "boolean", metric.name, value, subprojectName, weight)
} as? Boolean ?: false
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) {
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?) =
runSafe("report metric ${metric.name}") {
callJmx("reportNumber", "long", metric.name, value, subprojectName)
}
}
callJmx("reportNumber", "long", metric.name, value, subprojectName, weight)
} as? Boolean ?: false
override fun report(metric: StringMetrics, value: String, subprojectName: String?) {
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?) =
runSafe("report metric ${metric.name}") {
callJmx("reportString", "java.lang.String", metric.name, value, subprojectName)
}
}
callJmx("reportString", "java.lang.String", metric.name, value, subprojectName, weight)
} as? Boolean ?: false
override fun buildFinished(result: BuildResult) {
instance = null
@@ -217,27 +214,21 @@ internal class DefaultKotlinBuildStatsService internal constructor(
instance = null
}
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
}
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) {
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
}
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?): Boolean =
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
override fun report(metric: StringMetrics, value: String, subprojectName: String?) {
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
}
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?): Boolean =
KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName, weight)
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?) {
report(BooleanMetrics.valueOf(name), value, subprojectName)
}
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean =
report(BooleanMetrics.valueOf(name), value, subprojectName, weight)
override fun reportNumber(name: String, value: Long, subprojectName: String?) {
report(NumericalMetrics.valueOf(name), value, subprojectName)
}
override fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean =
report(NumericalMetrics.valueOf(name), value, subprojectName, weight)
override fun reportString(name: String, value: String, subprojectName: String?) {
report(StringMetrics.valueOf(name), value, subprojectName)
}
override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean =
report(StringMetrics.valueOf(name), value, subprojectName, weight)
}
@@ -10,8 +10,12 @@ import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import org.junit.Test
import java.lang.management.ManagementFactory
import java.util.concurrent.atomic.AtomicInteger
import javax.management.MBeanServer
import javax.management.ObjectName
import javax.management.StandardMBean
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class BuildStatServiceTest {
@@ -26,4 +30,41 @@ class BuildStatServiceTest {
jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10)
jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true)
}
@Test
fun testJmxServerWorks() {
val callsCount = AtomicInteger(0)
val instance = object : KotlinBuildStatsMXBean {
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?, weight: Long?): Boolean {
callsCount.incrementAndGet()
return true
}
override fun reportNumber(name: String, value: Long, subprojectName: String?, weight: Long?): Boolean {
callsCount.incrementAndGet()
return true
}
override fun reportString(name: String, value: String, subprojectName: String?, weight: Long?): Boolean {
callsCount.incrementAndGet()
return true
}
}
val beanName = ObjectName(KotlinBuildStatsService.JMX_BEAN_NAME)
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
mbs.registerMBean(StandardMBean(instance, KotlinBuildStatsMXBean::class.java), beanName)
try {
val jmxService = JMXKotlinBuildStatsService(mbs, beanName)
assertTrue(jmxService.report(StringMetrics.KOTLIN_COMPILER_VERSION, "1.2.3"))
assertTrue(jmxService.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 10))
assertTrue(jmxService.report(BooleanMetrics.ENABLED_DATABINDING, true))
assertEquals(3, callsCount.get())
} finally {
mbs.unregisterMBean(beanName)
}
}
}
@@ -154,15 +154,12 @@ class BuildSessionLogger(
closeTrackingFile()
}
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
metricsContainer.report(metric, value, subprojectName)
}
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?) =
metricsContainer.report(metric, value, subprojectName, weight)
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) {
metricsContainer.report(metric, value, subprojectName)
}
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?) =
metricsContainer.report(metric, value, subprojectName, weight)
override fun report(metric: StringMetrics, value: String, subprojectName: String?) {
metricsContainer.report(metric, value, subprojectName)
}
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?) =
metricsContainer.report(metric, value, subprojectName, weight)
}
@@ -104,31 +104,34 @@ class MetricsContainer : IStatisticsValuesConsumer {
private fun getProjectHash(perProject: Boolean, subprojectName: String?) =
if (subprojectName == null) null else processProjectName(subprojectName, perProject)
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?, weight: Long?): Boolean {
val projectHash = getProjectHash(metric.perProject, subprojectName)
synchronized(metricsLock) {
val metricContainer = booleanMetrics[MetricDescriptor(metric.name, projectHash)] ?: metric.type.newMetricContainer()
.also { booleanMetrics[MetricDescriptor(metric.name, projectHash)] = it }
metricContainer.addValue(metric.anonymization.anonymize(value))
metricContainer.addValue(metric.anonymization.anonymize(value), weight)
}
return true
}
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) {
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?, weight: Long?): Boolean {
val projectHash = getProjectHash(metric.perProject, subprojectName)
synchronized(metricsLock) {
val metricContainer = numericalMetrics[MetricDescriptor(metric.name, projectHash)] ?: metric.type.newMetricContainer()
.also { numericalMetrics[MetricDescriptor(metric.name, projectHash)] = it }
metricContainer.addValue(metric.anonymization.anonymize(value))
metricContainer.addValue(metric.anonymization.anonymize(value), weight)
}
return true
}
override fun report(metric: StringMetrics, value: String, subprojectName: String?) {
override fun report(metric: StringMetrics, value: String, subprojectName: String?, weight: Long?): Boolean {
val projectHash = getProjectHash(metric.perProject, subprojectName)
synchronized(metricsLock) {
val metricContainer = stringMetrics[MetricDescriptor(metric.name, projectHash)] ?: metric.type.newMetricContainer()
.also { stringMetrics[MetricDescriptor(metric.name, projectHash)] = it }
metricContainer.addValue(metric.anonymization.anonymize(value))
metricContainer.addValue(metric.anonymization.anonymize(value), weight)
}
return true
}
fun flush(trackingFile: IRecordLogger?) {
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.statistics.metrics
import java.util.*
interface IMetricContainer<T> {
fun addValue(t: T)
fun addValue(t: T, weight: Long? = null)
fun toStringRepresentation(): String
@@ -24,7 +24,7 @@ interface IMetricContainerFactory<T> {
open class OverrideMetricContainer<T>() : IMetricContainer<T> {
internal var myValue: T? = null
override fun addValue(t: T) {
override fun addValue(t: T, weight: Long?) {
myValue = t
}
@@ -44,7 +44,7 @@ class OverrideVersionMetricContainer() : OverrideMetricContainer<String>() {
myValue = v
}
override fun addValue(t: String) {
override fun addValue(t: String, weight: Long?) {
if (myValue == null || myValue == "0.0.0") {
myValue = t
}
@@ -56,22 +56,24 @@ class SumMetricContainer() : OverrideMetricContainer<Long>() {
myValue = v
}
override fun addValue(t: Long) {
override fun addValue(t: Long, weight: Long?) {
myValue = (myValue ?: 0) + t
}
}
class AverageMetricContainer() : IMetricContainer<Long> {
private var count = 0
private var myValue: Long? = null
private var totalWeight = 0L
private var totalSum: Long? = null
constructor(v: Long) : this() {
myValue = v
totalSum = v
totalWeight = 1
}
override fun addValue(t: Long) {
myValue = (myValue ?: 0) + t
count++
override fun addValue(t: Long, weight: Long?) {
val w = weight ?: 1
totalSum = (totalSum ?: 0) + t * w
totalWeight += w
}
override fun toStringRepresentation(): String {
@@ -79,7 +81,7 @@ class AverageMetricContainer() : IMetricContainer<Long> {
}
override fun getValue(): Long? {
return myValue?.div(count)
return totalSum?.div(if (totalWeight > 0) totalWeight else 1)
}
}
@@ -88,7 +90,7 @@ class OrMetricContainer() : OverrideMetricContainer<Boolean>() {
myValue = v
}
override fun addValue(t: Boolean) {
override fun addValue(t: Boolean, weight: Long?) {
myValue = (myValue ?: false) || t
}
}
@@ -104,7 +106,7 @@ class ConcatMetricContainer() : IMetricContainer<String> {
myValues.addAll(values)
}
override fun addValue(t: String) {
override fun addValue(t: String, weight: Long?) {
myValues.add(t.replace(SEPARATOR, ","))
}
@@ -18,9 +18,9 @@ interface AdditiveStatisticsValue<T> : ReportStatisticsValue<T> {
}
interface IStatisticsValuesConsumer {
fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String? = null)
fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String? = null, weight: Long? = null): Boolean
fun report(metric: NumericalMetrics, value: Long, subprojectName: String? = null)
fun report(metric: NumericalMetrics, value: Long, subprojectName: String? = null, weight: Long? = null): Boolean
fun report(metric: StringMetrics, value: String, subprojectName: String? = null)
fun report(metric: StringMetrics, value: String, subprojectName: String? = null, weight: Long? = null): Boolean
}