[stats-collector] eliminating unneeded abstractions over service status

GitOrigin-RevId: 72afd295fa24447b7a29541ac98f3eae39eb90b2
This commit is contained in:
Roman Shevchenko
2025-01-08 14:47:29 +01:00
committed by intellij-monorepo-bot
parent 9759360eec
commit bdaef0b795
4 changed files with 42 additions and 137 deletions

View File

@@ -1,52 +0,0 @@
// Copyright 2000-2024 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.stats.completion.network.status
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.stats.completion.network.assertNotEDT
import com.intellij.stats.completion.network.service.RequestService
import com.intellij.stats.completion.network.status.bean.AnalyticsPlatformSettingsDeserializer
class AnalyticsPlatformServiceStatus(private val statusUrl: String) : WebServiceStatus {
companion object {
private val LOG = logger<AnalyticsPlatformServiceStatus>()
private fun productCode(): String = ApplicationInfo.getInstance().build.productCode
fun withDefaultUrl(): AnalyticsPlatformServiceStatus =
AnalyticsPlatformServiceStatus("https://resources.jetbrains.com/storage/ap/mlcc/config/v1/${productCode()}.json")
}
@Volatile
private var isServerOk = false
@Volatile
private var dataServerUrl = ""
override val id: String = "AnalyticsPlatform"
override fun isServerOk(): Boolean = isServerOk
override fun dataServerUrl(): String = dataServerUrl
override fun update() {
isServerOk = false
dataServerUrl = ""
assertNotEDT()
val response = service<RequestService>().get(statusUrl)
if (response != null && response.isOK()) {
val settings = AnalyticsPlatformSettingsDeserializer.deserialize(response.text) ?: return
val satisfyingEndpoints = settings.versions.filter { it.satisfies() && it.endpoint != null }
if (satisfyingEndpoints.isEmpty()) {
LOG.debug("Analytics Platform completion web service status. No satisfying endpoints.")
return
}
if (satisfyingEndpoints.size > 1) {
LOG.error("Analytics Platform completion web service status. More than one satisfying endpoints. First one will be used.")
}
val endpointSettings = satisfyingEndpoints.first()
isServerOk = true
dataServerUrl = endpointSettings.endpoint!!
}
}
}

View File

@@ -1,11 +0,0 @@
// Copyright 2000-2020 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.stats.completion.network.status
interface WebServiceStatus {
val id: String
fun isServerOk(): Boolean
fun dataServerUrl(): String
fun update()
}

View File

@@ -1,44 +0,0 @@
// 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.stats.completion.network.status
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.registry.Registry
object WebServiceStatusManager {
private const val USE_ANALYTICS_PLATFORM_REGISTRY = "completion.stats.analytics.platform.send"
private const val ANALYTICS_PLATFORM_URL_REGISTRY = "completion.stats.analytics.platform.url"
private val LOG = logger<WebServiceStatusManager>()
private val statuses: MutableMap<String, WebServiceStatus> = mutableMapOf()
init {
if (Registry.`is`(USE_ANALYTICS_PLATFORM_REGISTRY, false)) {
registerAnalyticsPlatformStatus()
}
}
fun getAllStatuses(): List<WebServiceStatus> = statuses.values.toList()
private fun registerAnalyticsPlatformStatus() {
try {
val registry = Registry.get(ANALYTICS_PLATFORM_URL_REGISTRY)
if (registry.isChangedFromDefault()) {
register(AnalyticsPlatformServiceStatus(registry.asString()))
return
}
}
catch (e: Throwable) {
LOG.error("No url for Analytics Platform web status. Set registry: $ANALYTICS_PLATFORM_URL_REGISTRY")
}
register(AnalyticsPlatformServiceStatus.withDefaultUrl())
}
private fun register(status: WebServiceStatus) {
val old = statuses[status.id]
if (old != null) {
LOG.warn("Service status with id [${old.id}] already created.")
return
}
statuses[status.id] = status
}
}

View File

@@ -1,60 +1,72 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// 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.stats.completion.sender
import com.intellij.ide.ApplicationActivity
import com.intellij.internal.statistic.utils.StatisticsUploadAssistant
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.getOrLogException
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.ExtensionNotApplicableException
import com.intellij.stats.completion.network.status.WebServiceStatusManager
import com.intellij.openapi.util.registry.Registry
import com.intellij.stats.completion.network.service.RequestService
import com.intellij.stats.completion.network.status.bean.AnalyticsPlatformSettingsDeserializer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import kotlin.coroutines.coroutineContext
import kotlin.time.Duration.Companion.minutes
private fun isSendAllowed(): Boolean {
return isCompletionLogsSendAllowed() && StatisticsUploadAssistant.isSendAllowed()
}
internal fun isCompletionLogsSendAllowed(): Boolean =
ApplicationManager.getApplication().isEAP && System.getProperty("completion.stats.send.logs", "true").toBoolean()
internal fun isCompletionLogsSendAllowed(): Boolean {
return ApplicationManager.getApplication().isEAP && System.getProperty("completion.stats.send.logs", "true").toBoolean()
}
private const val USE_ANALYTICS_PLATFORM_KEY = "completion.stats.analytics.platform.send"
private const val ANALYTICS_PLATFORM_URL_KEY = "completion.stats.analytics.platform.url"
private val LOG = logger<SenderPreloadingActivity>()
private class SenderPreloadingActivity : ApplicationActivity {
private val statusUrl: String
init {
val app = ApplicationManager.getApplication()
if (app.isUnitTestMode || app.isHeadlessEnvironment) {
if (app.isUnitTestMode || app.isHeadlessEnvironment || !Registry.`is`(USE_ANALYTICS_PLATFORM_KEY, false)) {
throw ExtensionNotApplicableException.create()
}
statusUrl = Registry.get(ANALYTICS_PLATFORM_URL_KEY).takeIf { it.isChangedFromDefault() }?.asString()
?: "https://resources.jetbrains.com/storage/ap/mlcc/config/v1/${ApplicationInfo.getInstance().build.productCode}.json"
}
override suspend fun execute() {
// do not check right after the start - avoid getting UsageStatisticsPersistenceComponent too early
delay(5.minutes)
while (isSendAllowed()) {
while (isCompletionLogsSendAllowed() && StatisticsUploadAssistant.isSendAllowed()) {
delay(5.minutes)
runCatching {
send()
}.getOrLogException(logger<SenderPreloadingActivity>())
}
}
private suspend fun send() {
for (status in WebServiceStatusManager.getAllStatuses()) {
coroutineContext.ensureActive()
runCatching {
status.update()
if (status.isServerOk()) {
withContext(Dispatchers.IO) {
service<StatisticSender>().sendStatsData(status.dataServerUrl())
withContext(Dispatchers.IO) {
runCatching {
updateAndGetUrl()?.let { url ->
service<StatisticSender>().sendStatsData(url)
}
}.onFailure {
LOG.error(it)
}
}.getOrLogException(logger<SenderPreloadingActivity>())
}
}
}
}
private fun updateAndGetUrl(): String? {
val response = service<RequestService>().get(statusUrl)
if (response == null || !response.isOK()) return null
val settings = AnalyticsPlatformSettingsDeserializer.deserialize(response.text) ?: return null
val satisfyingEndpoints = settings.versions.filter { it.satisfies() && it.endpoint != null }
if (satisfyingEndpoints.isEmpty()) {
LOG.debug("Analytics Platform completion web service status. No satisfying endpoints.")
return null
}
if (satisfyingEndpoints.size > 1) {
LOG.error("Analytics Platform completion web service status. More than one satisfying endpoints. First one will be used.")
}
return satisfyingEndpoints.first().endpoint!!
}
}