IDEA-344654 New Import Settings: Add statistics

GitOrigin-RevId: f3879aa1646f32caadd837c0b61f0e6da6f03ce9
This commit is contained in:
Sergey Pak
2024-03-19 21:12:35 +01:00
committed by intellij-monorepo-bot
parent aca17d5e45
commit ecb8aca637
8 changed files with 291 additions and 8 deletions

View File

@@ -43,6 +43,9 @@
implementation="com.intellij.ide.startup.importSettings.transfer.backend.providers.vscode.mappings.CommonPluginMapping"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.ide.startup.importSettings.fus.TransferSettingsCollector"/>
<statistics.counterUsagesCollector
implementationClass="com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector"/>
<statistics.validation.customValidationRule implementation="com.intellij.ide.startup.importSettings.fus.KnownPluginValidationRule"/>
<transferSettings.externalProjectImportChecker implementation="com.intellij.ide.startup.importSettings.transfer.backend.CargoExternalProjectImportChecker"/>
</extensions>

View File

@@ -4,6 +4,7 @@ package com.intellij.ide.startup.importSettings
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
import com.intellij.ide.startup.importSettings.chooser.ui.OnboardingController
import com.intellij.ide.startup.importSettings.data.SettingsService
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.diagnostic.logger
import com.intellij.platform.ide.bootstrap.IdeStartupWizard
@@ -28,6 +29,7 @@ internal class IdeStartupWizardImpl : IdeStartupWizard {
if (!settingsService.shouldShowImport()) {
logger.info("No import options available: skipping the import wizard.")
ImportSettingsEventsCollector.firstPageSkipped()
return@coroutineScope
}

View File

@@ -9,6 +9,7 @@ import com.intellij.ide.startup.importSettings.data.DialogImportData
import com.intellij.ide.startup.importSettings.data.SettingsContributor
import com.intellij.ide.startup.importSettings.data.SettingsService
import com.intellij.openapi.ui.OnboardingBackgroundImageProvider
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.openapi.util.Disposer
interface ImportSettingsController : BaseController {
@@ -49,6 +50,7 @@ private class ImportSettingsControllerImpl(dialog: OnboardingDialog, override va
override fun goToSettingsPage(provider: ActionsDataProvider<*>, product: SettingsContributor) {
val page = SettingChooserPage.createPage(provider, product, this)
Disposer.tryRegister(dialog.disposable, page)
provider.productSelected(product)
dialog.changePage(page)
}
@@ -56,6 +58,7 @@ private class ImportSettingsControllerImpl(dialog: OnboardingDialog, override va
OnboardingBackgroundImageProvider.getInstance().loadImage { image ->
val page = ProductChooserPage(this, image)
Disposer.tryRegister(dialog.disposable, page)
ImportSettingsEventsCollector.firstPageShown()
dialog.changePage(page)
callback()

View File

@@ -2,6 +2,7 @@
package com.intellij.ide.startup.importSettings.chooser.ui
import com.intellij.ide.startup.importSettings.data.StartupWizardService
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.openapi.util.NlsContexts
import com.intellij.platform.ide.bootstrap.StartupWizardStage
@@ -105,6 +106,7 @@ class OnboardingController private constructor(){
if(!dl.isShowing) {
dl.initialize()
dl.show()
ImportSettingsEventsCollector.importDialogClosed()
}
state = State.WIZARD

View File

@@ -3,6 +3,8 @@ package com.intellij.ide.startup.importSettings.data
import com.intellij.ide.startup.importSettings.ImportSettingsBundle
import com.intellij.ide.startup.importSettings.data.ActionsDataProvider.Companion.toRelativeFormat
import com.intellij.ide.startup.importSettings.jb.JbProductInfo
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.ide.startup.importSettings.transfer.ExternalProductInfo
import org.jetbrains.annotations.Nls
import java.time.LocalDate
@@ -95,6 +97,9 @@ interface ActionsDataProvider<T : BaseService> {
fun getComment(contributor: SettingsContributor): @Nls String?
val main: List<Product>?
val other: List<Product>?
// callback actions
fun productSelected(contributor: SettingsContributor)
}
class JBrActionsDataProvider private constructor() : ActionsDataProvider<JbService> {
@@ -136,6 +141,11 @@ class JBrActionsDataProvider private constructor() : ActionsDataProvider<JbServi
override val other: List<Product>?
get() = map?.get(ActionsDataProvider.popUpPlace.OTHER)
override fun productSelected(contributor: SettingsContributor) {
val actual = main?.contains(contributor) == true
val productCodeName = (contributor as? JbProductInfo)?.codeName ?: ""
ImportSettingsEventsCollector.jbIdeSelected(productCodeName, actual)
}
}
class SyncActionsDataProvider private constructor() : ActionsDataProvider<SyncService> {
@@ -205,6 +215,9 @@ class SyncActionsDataProvider private constructor() : ActionsDataProvider<SyncSe
return map?.get(ActionsDataProvider.popUpPlace.OTHER)
}
override fun productSelected(contributor: SettingsContributor) {
// TODO implement with the sync
}
}
class ExtActionsDataProvider(override val productService: ExternalProductService) : ActionsDataProvider<ExternalProductService> {
@@ -225,6 +238,8 @@ class ExtActionsDataProvider(override val productService: ExternalProductService
override val main: List<Product>
get() = productService.products()
override val other: List<Product>? = null
override fun productSelected(contributor: SettingsContributor) {
ImportSettingsEventsCollector.externalSelected((contributor as Product).name)
}
}

View File

@@ -7,6 +7,7 @@ import com.intellij.ide.plugins.*
import com.intellij.ide.startup.importSettings.ImportSettingsBundle
import com.intellij.ide.startup.importSettings.StartupImportIcons
import com.intellij.ide.startup.importSettings.data.*
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.ide.startup.importSettings.transfer.TransferSettingsProgress
import com.intellij.openapi.application.*
import com.intellij.openapi.application.ex.ApplicationManagerEx
@@ -196,11 +197,14 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
}
override fun getOldProducts(): List<Product> {
return filterProducts(old = true)
return filterProducts(old = true).also {
ImportSettingsEventsCollector.oldJbIdes(it.map(JbProductInfo::codeName))
}
}
override fun importFromCustomFolder(folderPath: Path) {
val modalityState = ModalityState.current()
ImportSettingsEventsCollector.customDirectorySelected()
coroutineScope.async(modalityState.asContextElement()) {
val importer = JbSettingsImporter(folderPath, folderPath, null)
importer.importRaw()
@@ -224,11 +228,14 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
catch (tce: TimeoutCancellationException) {
LOG.info("Timeout waiting for products warmUp. Will show what we have now: ${tce.message}")
}
filterProducts(old = false)
filterProducts(old = false).also {
ImportSettingsEventsCollector.actualJbIdes(it.map(
JbProductInfo::codeName))
}
}
}
private fun filterProducts(old: Boolean): List<Product> {
private fun filterProducts(old: Boolean): List<JbProductInfo> {
val products = products.values.toList()
val newProducts = hashMapOf<String, String>()
for (product in products) {
@@ -421,21 +428,30 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
progressIndicator.text2 = ImportSettingsBundle.message("progress.details.migrating.options")
//TODO support plugin list customization for raw import
//storeImportConfig(productInfo.configDirPath, filteredCategories, plugins2Skip)
ImportSettingsEventsCollector.jbRawSelected(productInfo.codeName)
importer.importRaw()
LOG.info("Imported finished in ${System.currentTimeMillis() - startTime} ms. ")
return true
}
else {
try {
LOG.info("Starting migration...")
var restartRequired = false
ImportSettingsEventsCollector.jbImportStarted(
productInfo.codeName,
filteredCategories,
plugins2import?.keys?.map { it.idString } ?: emptyList(),
unselectedPlugins ?: emptyList()
)
try {
if (!plugins2import.isNullOrEmpty()) {
LOG.info("Started importing plugins...")
restartRequired = true
val pluginsStartTime = System.currentTimeMillis()
importer.installPlugins(coroutineScope, progressIndicator, plugins2import)
LOG.info("Plugins migrated in ${System.currentTimeMillis() - pluginsStartTime} ms.")
(System.currentTimeMillis() - pluginsStartTime).let {
LOG.info("Plugins migrated in $it ms.")
ImportSettingsEventsCollector.jbPluginsImportTimeSpent(it)
}
}
if (progressIndicator.isCanceled()) {
LOG.info("Import cancelled after importing the plugins. ${if (restartRequired) "Will now restart." else ""}")
@@ -446,7 +462,10 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
if (importer.importOptions(progressIndicator, filteredCategories)) {
restartRequired = true
}
LOG.info("Options migrated in ${System.currentTimeMillis() - optionsStartTime} ms.")
(System.currentTimeMillis() - optionsStartTime).let {
LOG.info("Options migrated in $it ms.")
ImportSettingsEventsCollector.jbOptionsImportTimeSpent(it)
}
}
catch (pce: ProcessCanceledException) {
LOG.info("Import cancelled")
@@ -454,7 +473,10 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
}
progressIndicator.fraction = 0.99
storeImportConfig(productInfo.configDir, filteredCategories, plugins2import?.keys?.map { it.idString })
LOG.info("Imported finished in ${System.currentTimeMillis() - startTime} ms. ")
(System.currentTimeMillis() - startTime).let {
LOG.info("Imported finished in $it ms.")
ImportSettingsEventsCollector.jbTotalImportTimeSpent(it)
}
return restartRequired
}
catch (th: Throwable) {

View File

@@ -12,6 +12,8 @@ import com.intellij.ide.plugins.RepositoryHelper
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
import com.intellij.ide.startup.importSettings.ImportSettingsBundle
import com.intellij.ide.startup.importSettings.data.SettingsService
import com.intellij.ide.startup.importSettings.jb.JbImportServiceImpl.Companion
import com.intellij.ide.startup.importSettings.statistics.ImportSettingsEventsCollector
import com.intellij.ide.ui.LafManager
import com.intellij.ide.ui.laf.LafManagerImpl
import com.intellij.openapi.application.*
@@ -405,6 +407,7 @@ class JbSettingsImporter(private val configDirPath: Path,
if (!SettingsService.getInstance().pluginIdsPreloaded) {
LOG.warn("Couldn't preload plugin ids, which indicates problems with connection. Will use old import")
val importOptions = configImportOptions(progressIndicator, pluginsMap.keys)
ImportSettingsEventsCollector.jbPluginsOldImport()
ConfigImportHelper.migratePlugins(
pluginsPath,
configDirPath,
@@ -414,6 +417,7 @@ class JbSettingsImporter(private val configDirPath: Path,
) { false }
return
}
ImportSettingsEventsCollector.jbPluginsNewImport()
RepositoryHelper.updatePluginHostsFromConfigDir(configDirPath, LOG)
val updateableMap = HashMap(pluginsMap)
progressIndicator.text2 = ImportSettingsBundle.message("progress.details.checking.for.plugin.updates")
@@ -432,6 +436,7 @@ class JbSettingsImporter(private val configDirPath: Path,
val descriptor = pluginsMap[pluginDownloader.id] ?: continue
updateableMap[pluginDownloader.id] = descriptor
// failed to download - should copy instead
ImportSettingsEventsCollector.jbPluginImportConnectionError()
LOG.info("Failed to download a newer version of '${pluginDownloader.id}' : ${pluginDownloader.pluginVersion}. " +
"Will try to copy old version (${descriptor.version}) instead")
}
@@ -482,6 +487,7 @@ class JbSettingsImporter(private val configDirPath: Path,
}
fun importRaw() {
val startTime = System.currentTimeMillis()
val externalVmOptionsFile = configDirPath.listDirectoryEntries("*.vmoptions").firstOrNull()
if (externalVmOptionsFile != null) {
val currentVMFile = PathManager.getConfigDir().resolve(VMOptions.getFileName())
@@ -494,6 +500,10 @@ class JbSettingsImporter(private val configDirPath: Path,
ConfigImportHelper.updateVMOptions(PathManager.getConfigDir(), LOG)
}
CustomConfigMigrationOption.MigrateFromCustomPlace(configDirPath).writeConfigMarkerFile(PathManager.getConfigDir())
(System.currentTimeMillis() - startTime).let {
LOG.info("Raw import finished in $it ms.")
ImportSettingsEventsCollector.jbTotalImportTimeSpent(it)
}
}
internal class ImportStreamProvider(private val configDirPath: Path) : StreamProvider {

View File

@@ -0,0 +1,226 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.startup.importSettings.statistics
import com.intellij.ide.startup.importSettings.jb.IDEData
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.openapi.components.SettingsCategory
object ImportSettingsEventsCollector : CounterUsagesCollector() {
private val GROUP = EventLogGroup("import.settings.events", 1)
override fun getGroup(): EventLogGroup = GROUP
private val UNKNOWN = "UNKNOWN"
private val FOLDER = "FOLDER"
// Lists/enums:
private val ALLOWED_JB_IDES: List<String> = IDEData.IDE_MAP.keys.plus(UNKNOWN).toList()
private val CATEGORIES: List<SettingsCategory> = SettingsCategory.entries.minus(SettingsCategory.OTHER).toList()
// 1 - select product
// 2 - customize
// 3 - import progress
private var currentPageIdx: Int = 0
private var currentPageShownTime: Long = 0
private enum class ImportType {
JB,
JB_RAW, // means that it would just copy the old folder to the new one.
EXTERNAL,
SYNC,
FOLDER,
}
private enum class FirstPageButton {
SYNC,
SYNC_OLD,
JB,
JB_OLD,
EXTERNAL,
FOLDER,
SKIP,
CLOSE,
}
enum class SecondPageButton {
NEXT,
BACK,
CLOSE
}
enum class ImportErrorTypes {
CONNECTION_ERROR,
}
enum class CloseDialogActions {
}
private val JB_IDE_VALUES = EventFields.StringList("jbIdeValues", ALLOWED_JB_IDES, "Supported JB IDEs")
private val FIRST_PAGE_BUTTONS = EventFields.Enum<FirstPageButton>("firstPageButton", "Buttons on the first page")
private val SECOND_PAGE_BUTTONS = EventFields.Enum<SecondPageButton>("secondPageButton", "Buttons on the second page")
private val IMPORT_TYPES = EventFields.Enum<ImportType>("importTypes", "Import type")
private val PLUGIN_CANT_IMPORT_REASONS = EventFields.Enum<ImportErrorTypes>("importErrorTypes")
private val JB_IMPORT_CATEGORIES = EventFields.StringList("settingsCategories",
CATEGORIES.map { it.name },
"Settings categories when importing from JB or SYNC")
private val IMPORT_SOURCE = EventFields.String("importSource", ALLOWED_JB_IDES.plus("FOLDER"))
private val TIME = EventFields.Long("timeMs")
// before first page - preparations and performance
// first page - select import source or skip
private val firstPageShown = GROUP.registerEvent("first.page.shown", EventFields.Boolean("shown"),
"indicates whether initial import settings page was shown to user, if not, then import was skipped completely")
private val jbIdeActualValues = GROUP.registerEvent("jb.ide.actual.values", JB_IDE_VALUES, "JB IDEs in the main dropdown")
private val jbIdeOldValues = GROUP.registerEvent("jb.ide.old.values", JB_IDE_VALUES, "JB IDEs in the other dropdown")
private val firstPageButton = GROUP.registerEvent("first.page.button", FIRST_PAGE_BUTTONS, "Button pressed on the first page")
private val jbIdeSelectedValue = GROUP.registerEvent("first.page.selected.jb.ide", EventFields.String("jbIde", ALLOWED_JB_IDES), "JB IDE selected")
private val firstPageTimeSpent = GROUP.registerEvent("first.page.time.spent", TIME)
//second page - JB IDE - select import details
private val secondPageShown = GROUP.registerEvent("second.page.shown", IMPORT_TYPES)
private val jbIdeUnselectedOptions = GROUP.registerEvent("second.page.jb.categories",
JB_IMPORT_CATEGORIES,
"unselected options when importing from JB IDE")
private val jbIdePlugins = GROUP.registerEvent(
"second.page.jb.ide.plugins",
EventFields.Int("totalCount", "Total number of plugins that we've found during scanning"),
EventFields.Int("unselectedCount", "number of unselected plugins"),
"number of plugins and number of unselected plugins")
private val jbConfigurePluginsClicked = GROUP.registerEvent("second.page.jb.configure.plugins.clicked", "User clicked on configure plugins link for JB IDE")
private val secondPageJbButton = GROUP.registerEvent("second.page.jb.button", SECOND_PAGE_BUTTONS, "Button pressed on the second JB page")
private val secondPageTimeSpent = GROUP.registerEvent("second.page.time.spent", TIME)
// third page - progress dialog
private val thirdPageShown = GROUP.registerEvent("third.page.shown", EventFields.Boolean("shown"),
"Indicates whether the third page (import progress dialog) was shown. It's common for all import types")
private val importType = GROUP.registerEvent("import.type",
IMPORT_TYPES,
IMPORT_SOURCE,
"Which type of import is used (JB/NONJB/SYNC) and the source name")
private val pluginsImportTime = GROUP.registerEvent("import.plugins.time.spent", TIME, "How long did it take to import plugins")
private val pluginsCounts = GROUP.registerEvent("import.plugins.counts",
EventFields.Long("imported"),
EventFields.Long("skipped"),
"How many plugins were imported during imported or skipped")
private val jbPluginImportType = GROUP.registerEvent("import.plugins.import.type", EventFields.Boolean("isNew"), "What plugin import type is used (new or legacy)")
private val jbPluginCantImport = GROUP.registerEvent("import.plugins.cant.import.reason", PLUGIN_CANT_IMPORT_REASONS)
private val optionsImportTime = GROUP.registerEvent("import.options.time.spent", TIME, "How long did it take to import options and schemas")
private val totalImportTime = GROUP.registerEvent("import.total.time.spent", TIME, "how long did it take to import everything")
// after restart, but before showing welcome screen - reload last settings
private val afterImportRestartTime = GROUP.registerEvent("after.import.restart.time", TIME, "How long did it take to restart")
/////// Methods
private fun changePage(pageIdx: Int) {
val currentTime = System.currentTimeMillis()
if (currentPageIdx == 1) {
firstPageTimeSpent.log(currentTime - currentPageShownTime)
}
else if (currentPageIdx == 2) {
secondPageTimeSpent.log(currentTime - currentPageShownTime)
}
currentPageShownTime = currentTime
currentPageIdx = pageIdx
}
fun firstPageShown() {
firstPageShown.log(true)
changePage(1)
}
fun firstPageSkipped() {
firstPageShown.log(false)
}
fun actualJbIdes(jbIdes: List<String>) {
jbIdeActualValues.log(jbIdes)
}
fun oldJbIdes(jbIdes: List<String>) {
jbIdeOldValues.log(jbIdes)
}
fun jbIdeSelected(jbIde: String, isActual: Boolean) {
firstPageButton.log(if (isActual) FirstPageButton.JB else FirstPageButton.JB_OLD)
secondPageShown.log(ImportType.JB)
changePage(2)
if (IDEData.IDE_MAP.keys.contains(jbIde))
jbIdeSelectedValue.log(jbIde)
else
jbIdeSelectedValue.log(UNKNOWN)
}
fun externalSelected(externalId: String) {
firstPageButton.log(FirstPageButton.EXTERNAL)
secondPageShown.log(ImportType.EXTERNAL)
changePage(2)
}
fun customDirectorySelected() {
firstPageButton.log(FirstPageButton.FOLDER)
importType.log(ImportType.FOLDER, FOLDER)
changePage(0)
}
fun jbRawSelected(jbIde: String) {
importType.log(ImportType.JB_RAW, jbIde)
changePage(3)
}
fun jbImportStarted(jbIde: String,
selectedCategories: Collection<SettingsCategory>,
selectedPlugins: List<String>,
unselectedPlugins: List<String>
) {
importType.log(ImportType.JB, jbIde)
changePage(3)
val unselectedCategories = CATEGORIES.minus(selectedCategories.toSet())
jbIdeUnselectedOptions.log(unselectedCategories.map { it.name })
val totalPluginsCount = selectedPlugins.size + unselectedPlugins.size
jbIdePlugins.log(totalPluginsCount, unselectedPlugins.size)
}
fun firstPageTimeSpent(timeMs: Long) {
firstPageTimeSpent.log(timeMs)
}
fun secondPageTimeSpent(timeMs: Long) {
secondPageTimeSpent.log(timeMs)
}
fun jbPluginImportConnectionError() {
jbPluginCantImport.log(ImportErrorTypes.CONNECTION_ERROR)
}
fun jbPluginsOldImport() {
jbPluginImportType.log(false)
}
fun jbPluginsNewImport() {
jbPluginImportType.log(true)
}
fun jbPluginsImportTimeSpent(timeMs: Long) {
pluginsImportTime.log(timeMs)
}
fun jbOptionsImportTimeSpent(timeMs: Long) {
optionsImportTime.log(timeMs)
}
fun jbTotalImportTimeSpent(timeMs: Long) {
totalImportTime.log(timeMs)
}
fun importDialogClosed() {
changePage(0)
}
}