[wsl] IJPL-73547 WSL Usage Metrics: project.created.in.wsl, project.opened.in.wsl, project.cloned.in.wsl [IJ-CR-158850]

(cherry picked from commit 59da204cab10cbf5789e47ce59625500ec658d59)

GitOrigin-RevId: dd30918b87e8f88dbf8c8e378c133bb9f8f452e9
This commit is contained in:
Rustam Vishniakov
2025-03-27 19:50:12 +01:00
committed by intellij-monorepo-bot
parent eeba8827c5
commit 3fff98e4af
5 changed files with 144 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ package com.intellij.ide.impl
import com.intellij.configurationStore.runInAutoSaveDisabledMode
import com.intellij.configurationStore.saveSettings
import com.intellij.featureStatistics.fusCollectors.WslUsagesCollector
import com.intellij.ide.JavaUiBundle
import com.intellij.ide.SaveAndSyncHandler
import com.intellij.ide.impl.ProjectUtil.focusProjectWindow
@@ -65,6 +66,7 @@ suspend fun createNewProjectAsync(wizard: AbstractProjectWizard) {
try {
val projectFile = Path.of(wizard.newProjectFilePath)
WslUsagesCollector.beforeProjectCreated(projectFile)
val newProject = createProjectFromWizardImpl(wizard = wizard, projectFile = projectFile, projectToClose = null)
NewProjectWizardCollector.logProjectCreated(newProject, wizard.wizardContext)
}

View File

@@ -0,0 +1,136 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.featureStatistics.fusCollectors
import com.intellij.execution.wsl.WSLDistribution
import com.intellij.execution.wsl.WslDistributionManager
import com.intellij.execution.wsl.WslIjentAvailabilityService
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.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.wm.impl.welcomeScreen.cloneableProjects.CloneableProjectsService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import java.nio.file.Path
import java.util.*
import kotlin.io.path.invariantSeparatorsPathString
@ApiStatus.Internal
object WslUsagesCollector : CounterUsagesCollector() {
private val WSL_USAGES_GROUP = EventLogGroup("wsl.usages", 1)
private val DISTRO_TYPE_FIELD = EventFields.Enum<DistroType>("distribution_type")
private val WSL_VERSION_FIELD = EventFields.Int("wsl_version")
private val EEL_API_USED_FIELD = EventFields.Boolean("is_eel_api_used")
private val VCS_TYPE_FIELD = EventFields.Enum<VcsType>("vcs_type")
private val PROJECT_CREATED_EVENT = WSL_USAGES_GROUP.registerEvent("project.created.in.wsl",
DISTRO_TYPE_FIELD,
WSL_VERSION_FIELD,
EEL_API_USED_FIELD)
private val PROJECT_OPENED_EVENT = WSL_USAGES_GROUP.registerEvent("project.opened.in.wsl",
DISTRO_TYPE_FIELD,
WSL_VERSION_FIELD,
EEL_API_USED_FIELD)
private val PROJECT_CLONED_EVENT = WSL_USAGES_GROUP.registerVarargEvent("project.cloned.in.wsl",
DISTRO_TYPE_FIELD,
WSL_VERSION_FIELD,
EEL_API_USED_FIELD,
VCS_TYPE_FIELD)
private val WSL_PATH_PREFIXES = listOf("//wsl$/", "//wsl.localhost/")
private val newProjectData = Collections.synchronizedMap(mutableMapOf<String, ProjectData>())
enum class VcsType { None, Git, Svn, Hg, Other }
enum class DistroType {
Ubuntu,
Centos,
Debian,
Fedora,
Kali,
Suse,
Oracle,
Other;
companion object {
fun fromName(distroName: String): DistroType =
when {
distroName.contains("Ubuntu", true) -> Ubuntu
distroName.contains("Debian", true) -> Debian
distroName.contains("Kali", true) -> Kali
distroName.contains("SUSE", true) -> Suse
distroName.contains("Oracle", true) -> Oracle
distroName.contains("Fedora", true) -> Fedora
distroName.contains("Centos", true) -> Centos
else -> Other
}
}
}
private data class ProjectData(val vcsType: VcsType)
override fun getGroup(): EventLogGroup? = WSL_USAGES_GROUP
fun beforeProjectCreated(path: Path, task: CloneableProjectsService.CloneTask? = null) {
if (!SystemInfo.isWindows) return
path.invariantSeparatorsPathString.takeIf { it.isWslPath() }?.let {
newProjectData.put(it, ProjectData(getVcsType(task)))
}
}
private fun getVcsType(task: CloneableProjectsService.CloneTask? = null): VcsType =
task?.let {
val className = it.javaClass.name
when {
className.contains("SvnCheckout") -> VcsType.Svn
className.contains("GitCheckout") -> VcsType.Git
className.contains("HgCheckout") -> VcsType.Hg
else -> VcsType.Other
}
} ?: VcsType.None
private fun String.isWslPath() = WSL_PATH_PREFIXES.any { this.startsWith(it) }
suspend fun logProjectOpened(project: Project) {
if (!SystemInfo.isWindows) return
val projectPath = project.basePath
if (projectPath?.isWslPath() == true) {
findWslDistro(projectPath)?.let { wslDistro ->
val distroType = DistroType.fromName(wslDistro.presentableName)
val isEelUsed = WslIjentAvailabilityService.getInstance().runWslCommandsViaIjent()
if (newProjectData.contains(projectPath)) {
val projectData = newProjectData[projectPath]
newProjectData.remove(projectPath)
val vcsType = projectData?.vcsType ?: VcsType.None
if (vcsType == VcsType.None) {
PROJECT_CREATED_EVENT.log(distroType, wslDistro.version, isEelUsed)
}
else {
PROJECT_CLONED_EVENT.log(
DISTRO_TYPE_FIELD.with(distroType),
WSL_VERSION_FIELD.with(wslDistro.version),
EEL_API_USED_FIELD.with(isEelUsed),
VCS_TYPE_FIELD.with(vcsType))
}
}
else {
PROJECT_OPENED_EVENT.log(distroType, wslDistro.version, isEelUsed)
}
}
}
}
private suspend fun findWslDistro(projectPathStr: String): WSLDistribution? =
withContext(Dispatchers.IO) {
WslDistributionManager.getInstance().installedDistributions.firstOrNull {
val distroPath = Path.of(it.getUNCRootPath().toUri())
Path.of(projectPathStr).startsWith(distroPath)
}
}
}

View File

@@ -13,6 +13,7 @@ import com.intellij.diagnostic.ActivityCategory
import com.intellij.diagnostic.PluginException
import com.intellij.diagnostic.StartUpMeasurer
import com.intellij.featureStatistics.fusCollectors.LifecycleUsageTriggerCollector
import com.intellij.featureStatistics.fusCollectors.WslUsagesCollector
import com.intellij.ide.*
import com.intellij.ide.impl.*
import com.intellij.ide.lightEdit.LightEdit
@@ -756,6 +757,7 @@ open class ProjectManagerImpl : ProjectManagerEx(), Disposable {
blockingContext {
LifecycleUsageTriggerCollector.onProjectOpened(project)
}
WslUsagesCollector.logProjectOpened(project)
options.callback?.projectOpened(project, module ?: ModuleManager.getInstance(project).modules.firstOrNull())

View File

@@ -2,6 +2,7 @@
package com.intellij.openapi.wm.impl.welcomeScreen.cloneableProjects
import com.intellij.CommonBundle
import com.intellij.featureStatistics.fusCollectors.WslUsagesCollector
import com.intellij.ide.RecentProjectMetaInfo
import com.intellij.ide.RecentProjectsManager
import com.intellij.ide.RecentProjectsManagerBase
@@ -26,6 +27,7 @@ import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.CalledInAny
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.SystemIndependent
import java.nio.file.Path
import java.util.*
@Service(Level.APP)
@@ -38,6 +40,7 @@ class CloneableProjectsService {
val progressIndicator = CloneableProjectProgressIndicator(taskInfo)
val cloneableProject = CloneableProject(projectPath, taskInfo, progressIndicator, CloneStatus.PROGRESS)
addCloneableProject(cloneableProject)
WslUsagesCollector.beforeProjectCreated(Path.of(projectPath), cloneTask)
ApplicationManager.getApplication().executeOnPooledThread {
ProgressManager.getInstance().runProcess(Runnable {

View File

@@ -1052,6 +1052,7 @@
<statistic.eventLog.fusStateEventTracker
implementation="com.intellij.configurationStore.statistic.eventLog.FeatureUsageSettingsEventScheduler"/>
<statistics.applicationUsagesCollector implementation="com.intellij.featureStatistics.fusCollectors.WSLInstallationsCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.featureStatistics.fusCollectors.WslUsagesCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.ide.impl.TrustedProjectsStatistics"/>
<statistics.applicationUsagesCollector implementation="com.intellij.notification.impl.NotificationSettingsCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.openapi.project.ReadmeShownUsageCollector"/>