IDEA-CR-48136: FUS: record numerical fields if settings reporting is enabled (FUS-444)

(cherry picked from commit bdf5420031054affd584a7b9099e4a2fc496623f)

GitOrigin-RevId: e5c4a5b6fc50bf3bf39f7f4cfc7687888af99cb4
This commit is contained in:
Svetlana.Zemlyanskaya
2019-06-09 23:07:12 +03:00
committed by intellij-monorepo-bot
parent a37e5e0577
commit 29bdecfcdd
5 changed files with 199 additions and 33 deletions
@@ -6,17 +6,19 @@ import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.fus.FeatureUsageLogger
import com.intellij.internal.statistic.utils.getPluginInfo
import com.intellij.internal.statistic.utils.getProjectId
import com.intellij.openapi.components.ReportValue
import com.intellij.openapi.components.State
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.util.concurrency.NonUrgentExecutor
import com.intellij.util.containers.ContainerUtil
import com.intellij.serialization.MutableAccessor
import com.intellij.util.xmlb.BeanBinding
import org.jdom.Element
import java.util.*
private val LOG = Logger.getInstance("com.intellij.configurationStore.statistic.eventLog.FeatureUsageSettingsEventPrinter")
private val GROUP = EventLogGroup("settings", 3)
private val GROUP = EventLogGroup("settings", 4)
private val recordedComponents: MutableSet<String> = ContainerUtil.newConcurrentSet()
private val recordedOptionNames: MutableSet<String> = ContainerUtil.newConcurrentSet()
@@ -91,20 +93,15 @@ open class FeatureUsageSettingsEventPrinter(private val recordDefault: Boolean)
for (accessor in accessors) {
val type = accessor.genericType
if (type === Boolean::class.javaPrimitiveType) {
val value = accessor.readUnsafe(state)
val isDefault = !jdomSerializer.getDefaultSerializationFilter().accepts(accessor, state)
if (!isDefault || recordDefault) {
recordedOptionNames.add(accessor.name)
val content = HashMap<String, Any>()
content["component"] = componentName
content["name"] = accessor.name
content["value"] = value
if (recordDefault) {
content["default"] = isDefault
}
addProjectOptions(content, isDefaultProject, hash)
logConfig(GROUP, eventId, content)
}
logConfigValue(accessor, state, "bool", eventId, isDefaultProject, true, hash, componentName)
}
else if (type === Int::class.javaPrimitiveType || type === Long::class.javaPrimitiveType) {
val reportValue = accessor.getAnnotation(ReportValue::class.java) != null
logConfigValue(accessor, state, "int", eventId, isDefaultProject, reportValue, hash, componentName)
}
else if (type === Float::class.javaPrimitiveType || type === Double::class.javaPrimitiveType) {
val reportValue = accessor.getAnnotation(ReportValue::class.java) != null
logConfigValue(accessor, state, "float", eventId, isDefaultProject, reportValue, hash, componentName)
}
}
@@ -113,6 +110,33 @@ open class FeatureUsageSettingsEventPrinter(private val recordDefault: Boolean)
}
}
private fun logConfigValue(accessor: MutableAccessor,
state: Any,
type: String,
eventId: String,
isDefaultProject: Boolean,
reportValue: Boolean,
hash: String?,
componentName: String) {
val value = accessor.readUnsafe(state)
val isDefault = !jdomSerializer.getDefaultSerializationFilter().accepts(accessor, state)
if (!isDefault || recordDefault) {
recordedOptionNames.add(accessor.name)
val content = HashMap<String, Any>()
content["type"] = type
content["component"] = componentName
content["name"] = accessor.name
if (reportValue) {
content["value"] = value
}
if (recordDefault) {
content["default"] = isDefault
}
addProjectOptions(content, isDefaultProject, hash)
logConfig(GROUP, eventId, content)
}
}
private fun addProjectOptions(content: HashMap<String, Any>,
isDefaultProject: Boolean,
projectHash: String?) {
@@ -5,11 +5,13 @@ import com.intellij.configurationStore.getStateSpec
import com.intellij.configurationStore.statistic.eventLog.FeatureUsageSettingsEventPrinter
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.ReportValue
import com.intellij.openapi.components.State
import com.intellij.openapi.project.ProjectManager
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.assertions.Assertions.assertThat
import com.intellij.util.xmlb.annotations.Attribute
import org.junit.Assert
import org.junit.ClassRule
import org.junit.Test
@@ -116,8 +118,8 @@ class FeatureUsageSettingsEventsTest {
val withProject = true
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertDefaultState(printer.getOptionByName("boolOption"), "boolOption", false, withProject, defaultProject)
assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, withProject, defaultProject)
assertDefaultState(printer.getOptionByName("boolOption"), "boolOption", false, "bool", withProject, defaultProject)
assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, "bool", withProject, defaultProject)
}
@Test
@@ -174,7 +176,7 @@ class FeatureUsageSettingsEventsTest {
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
}
@Test
@@ -200,7 +202,7 @@ class FeatureUsageSettingsEventsTest {
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
}
@Test
@@ -215,8 +217,8 @@ class FeatureUsageSettingsEventsTest {
val withProject = true
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, "bool", withProject, defaultProject)
}
@Test
@@ -232,7 +234,7 @@ class FeatureUsageSettingsEventsTest {
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
}
@Suppress("SameParameterValue")
@@ -248,8 +250,8 @@ class FeatureUsageSettingsEventsTest {
val withProject = true
val defaultProject = false
assertThat(printer.result).hasSize(2)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, "bool", withRecordDefault, withProject, defaultProject)
}
@Test
@@ -265,8 +267,76 @@ class FeatureUsageSettingsEventsTest {
val defaultProject = false
assertThat(printer.result).hasSize(3)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, "bool", withRecordDefault, withProject, defaultProject)
}
@Test
fun `record default numerical fields in application component`() {
val component = TestComponent()
component.loadState(ComponentStateWithNumerical())
val spec = getStateSpec(component)
val printer = TestFeatureUsageSettingsEventsPrinter(false)
printer.logConfigurationState(spec.name, component.state, null)
val withProject = false
val defaultProject = false
Assert.assertEquals(1, printer.result.size)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
}
@Test
fun `record not default numerical fields in application component`() {
val component = TestComponent()
component.loadState(ComponentStateWithNumerical(intOpt = 10, longOpt = 15, floatOpt = 5.5F, doubleOpt = 3.4))
val spec = getStateSpec(component)
val printer = TestFeatureUsageSettingsEventsPrinter(false)
printer.logConfigurationState(spec.name, component.state, null)
val withProject = false
val defaultProject = false
Assert.assertEquals(5, printer.result.size)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("integerOption"), "integerOption", null, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("longOption"), "longOption", null, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("floatOption"), "floatOption", null, "float", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("doubleOption"), "doubleOption", null, "float", false, withProject, defaultProject)
}
@Test
fun `record not default numerical fields with absolute value in application component`() {
val component = TestComponent()
component.loadState(ComponentStateWithNumerical(absIntOpt = 10, absLongOpt = 15, absFloatOpt = 5.5F, absDoubleOpt = 3.4))
val spec = getStateSpec(component)
val printer = TestFeatureUsageSettingsEventsPrinter(false)
printer.logConfigurationState(spec.name, component.state, null)
val withProject = false
val defaultProject = false
Assert.assertEquals(5, printer.result.size)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absIntegerOption"), "absIntegerOption", 10, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absLongOption"), "absLongOption", 15L, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absFloatOption"), "absFloatOption", 5.5f, "float", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absDoubleOption"), "absDoubleOption", 3.4, "float", false, withProject, defaultProject)
}
@Test
fun `record all not default numerical fields with absolute value in application component`() {
val component = TestComponent()
component.loadState(ComponentStateWithNumerical(absIntOpt = 10, absLongOpt = 15, absFloatOpt = 5.5F, absDoubleOpt = 3.4))
val spec = getStateSpec(component)
val printer = TestFeatureUsageSettingsEventsPrinter(false)
printer.logConfigurationState(spec.name, component.state, null)
val withProject = false
val defaultProject = false
Assert.assertEquals(5, printer.result.size)
assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absIntegerOption"), "absIntegerOption", 10, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absLongOption"), "absLongOption", 15L, "int", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absFloatOption"), "absFloatOption", 5.5f, "float", false, withProject, defaultProject)
assertNotDefaultState(printer.getOptionByName("absDoubleOption"), "absDoubleOption", 3.4, "float", false, withProject, defaultProject)
}
private fun assertDefaultWithoutDefaultRecording(printer: TestFeatureUsageSettingsEventsPrinter,
@@ -276,14 +346,16 @@ class FeatureUsageSettingsEventsTest {
assertInvokedRecorded(printer.result[0], withProject, defaultProject)
}
private fun assertNotDefaultState(printer: TestFeatureUsageSettingsEventsPrinter, withRecordDefault: Boolean, withProject: Boolean, defaultProject: Boolean) {
@Suppress("SameParameterValue")
private fun assertNotDefaultState(printer: TestFeatureUsageSettingsEventsPrinter,withRecordDefault: Boolean, withProject: Boolean, defaultProject: Boolean) {
assertThat(printer.result).hasSize(1)
assertNotDefaultState(printer.result[0], "boolOption", true, withRecordDefault, withProject, defaultProject)
assertNotDefaultState(printer.result[0], "boolOption", true, "bool", withRecordDefault, withProject, defaultProject)
}
private fun assertNotDefaultState(event: LoggedComponentStateEvents,
name: String,
value: Any,
value: Any?,
type: String,
withDefaultRecorded: Boolean,
withProject: Boolean,
defaultProject: Boolean) {
@@ -292,14 +364,18 @@ class FeatureUsageSettingsEventsTest {
assertThat(event.id).isEqualTo(if (withDefaultRecorded) "option" else "not.default")
var size = 3
if (value != null) size++
if (withDefaultRecorded) size++
if (withProject) size++
if (defaultProject) size++
assertThat(event.data).hasSize(size)
assertThat(event.data["component"]).isEqualTo("MyTestComponent")
assertThat(event.data["type"]).isEqualTo(type)
assertThat(event.data["name"]).isEqualTo(name)
assertThat(event.data["value"]).isEqualTo(value)
if (value != null) {
assertThat(event.data["value"]).isEqualTo(value)
}
if (withDefaultRecorded) {
assertThat(event.data["default"]).isEqualTo(false)
}
@@ -313,24 +389,26 @@ class FeatureUsageSettingsEventsTest {
private fun assertDefaultState(printer: TestFeatureUsageSettingsEventsPrinter, withProject: Boolean, defaultProject: Boolean) {
assertThat(printer.result).hasSize(1)
assertDefaultState(printer.result[0], "boolOption", false, withProject, defaultProject)
assertDefaultState(printer.result[0], "boolOption", false, "bool", withProject, defaultProject)
}
private fun assertDefaultState(event: LoggedComponentStateEvents,
name: String,
value: Any,
type: String,
withProject: Boolean,
defaultProject: Boolean) {
assertThat(event.group.id).isEqualTo("settings")
assertThat(event.group.version).isGreaterThan(0)
assertThat(event.id).isEqualTo("option")
var size = 4
var size = 5
if (withProject) size++
if (defaultProject) size++
assertThat(event.data).hasSize(size)
assertThat(event.data["component"]).isEqualTo("MyTestComponent")
assertThat(event.data["type"]).isEqualTo(type)
assertThat(event.data["name"]).isEqualTo(name)
assertThat(event.data["value"]).isEqualTo(value)
assertThat(event.data["default"]).isEqualTo(true)
@@ -422,4 +500,45 @@ class FeatureUsageSettingsEventsTest {
@Attribute("second-bool-value")
val secondBoolOption: Boolean = secondBool
}
@Suppress("unused")
private class ComponentStateWithNumerical(intOpt: Int = 0,
longOpt: Long = 0,
floatOpt: Float = 0.0F,
doubleOpt: Double = 0.0,
absIntOpt: Int = 0,
absLongOpt: Long = 0,
absFloatOpt: Float = 0.0F,
absDoubleOpt: Double = 0.0,
bool: Boolean = false,
str: String = "string-option",
list: List<Int> = ArrayList()) : ComponentState(bool, str, list) {
@Attribute("int-option")
val integerOption: Int = intOpt
@Attribute("long-option")
val longOption: Long = longOpt
@Attribute("float-option")
val floatOption: Float = floatOpt
@Attribute("double-option")
val doubleOption: Double = doubleOpt
@Attribute("abs-int-option")
@field:ReportValue
val absIntegerOption: Int = absIntOpt
@Attribute("abs-long-option")
@field:ReportValue
val absLongOption: Long = absLongOpt
@Attribute("abs-float-option")
@field:ReportValue
val absFloatOption: Float = absFloatOpt
@Attribute("abs-double-option")
@field:ReportValue
val absDoubleOption: Double = absDoubleOpt
}
}
@@ -2,6 +2,7 @@
package com.intellij.ide.ui
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.ReportValue
import com.intellij.openapi.util.SystemInfo
import com.intellij.ui.scale.JBUIScale
import com.intellij.util.PlatformUtils
@@ -34,9 +35,11 @@ class UISettingsState : BaseState() {
@Deprecated("", replaceWith = ReplaceWith("NotRoamableUiOptions.fontScale"))
var fontScale by property(0f)
@get:ReportValue
@get:OptionTag("RECENT_FILES_LIMIT")
var recentFilesLimit by property(50)
@get:ReportValue
@get:OptionTag("RECENT_LOCATIONS_LIMIT")
var recentLocationsLimit by property(25)
@@ -47,6 +50,7 @@ class UISettingsState : BaseState() {
@get:OptionTag("CONSOLE_CYCLE_BUFFER_SIZE_KB")
var consoleCycleBufferSizeKb by property(1024)
@get:ReportValue
@get:OptionTag("EDITOR_TAB_LIMIT")
var editorTabLimit by property(10)
@@ -6,7 +6,7 @@ import com.intellij.internal.statistic.utils.StatisticsUploadAssistant
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.registry.Registry
class FeatureUsageEventLoggerProvider : StatisticsEventLoggerProvider("FUS", 18) {
class FeatureUsageEventLoggerProvider : StatisticsEventLoggerProvider("FUS", 19) {
override fun isRecordEnabled(): Boolean {
return !ApplicationManager.getApplication().isUnitTestMode &&
Registry.`is`("feature.usage.event.log.collect.and.upload") &&
@@ -0,0 +1,19 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.components;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that an absolute value of the numerical field should be reported in statistics.
* Won't work on objects, string or enum fields.
*
* Can be used within persistent components if reportStatistics flag is enabled.
* @see State#reportStatistic()
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReportValue {
}