@@ -9,6 +9,7 @@ import com.intellij.openapi.application.ApplicationInfo
99import com.intellij.openapi.components.Service
1010import com.intellij.openapi.util.SystemInfo
1111import io.ktor.client.*
12+ import io.ktor.client.engine.java.*
1213import io.ktor.client.plugins.observer.*
1314import io.ktor.client.request.*
1415import io.ktor.client.statement.*
@@ -21,40 +22,52 @@ class GoogleAnalytics {
2122
2223 private val scope = ioScope()
2324
24- private val httpClient = HttpClient {
25- expectSuccess = false
26- ResponseObserver { response ->
27- println (" HTTP status: ${response.status.value} \n ${response.bodyAsText()} " )
25+ // The engine must be named explicitly: engine auto-discovery goes through
26+ // java.util.ServiceLoader, and on IDEs whose platform bundles its own ktor
27+ // (2025.2+ ships ktor-client-cio in lib/) the lookup finds the platform's
28+ // CIOEngineContainer, which implements the platform copy of the interface,
29+ // not ours — ServiceConfigurationError: "... not a subtype".
30+ private val httpClient by lazy {
31+ HttpClient (Java ) {
32+ expectSuccess = false
33+ ResponseObserver { response ->
34+ println (" HTTP status: ${response.status.value} \n ${response.bodyAsText()} " )
35+ }
2836 }
2937 }
3038
3139 fun sendEvent (event : Event ) {
3240 scope.launch {
33- httpClient.post(" ${R .keys.analyticsEndPoint} ?measurement_id=${R .keys.analyticsMeasurementId} &api_secret=${R .keys.analyticsApiSecret} " ) {
34- contentType(ContentType .Application .Json )
35- setBody(buildJsonObject {
36- put(" client_id" , PropertiesComponent .getInstance().analyticsClientId)
37- putJsonObject(" user_properties" ) {
38- putJsonObject(" os" ) {
39- put(" value" , SystemInfo .getOsNameAndVersion())
40- }
41- putJsonObject(" productCode" ) {
42- put(" value" , ApplicationInfo .getInstance().build.productCode)
43- }
44- putJsonObject(" productVersion" ) {
45- put(" value" , ApplicationInfo .getInstance().build.asStringWithoutProductCodeAndSnapshot())
46- }
47- putJsonObject(" version" ) {
48- // avoid PluginId.getId: it compiles to a PluginId.Companion reference that older IDEs (< 2025.2) don't have
49- put(" value" , PluginManagerCore .plugins.find { it.pluginId.idString == R .keys.pluginId }?.version)
50- }
41+ // analytics must never surface an IDE error, whatever goes wrong
42+ runCatching { postEvent(event) }
43+ }
44+ }
45+
46+ private suspend fun postEvent (event : Event ) {
47+ httpClient.post(" ${R .keys.analyticsEndPoint} ?measurement_id=${R .keys.analyticsMeasurementId} &api_secret=${R .keys.analyticsApiSecret} " ) {
48+ contentType(ContentType .Application .Json )
49+ setBody(buildJsonObject {
50+ put(" client_id" , PropertiesComponent .getInstance().analyticsClientId)
51+ putJsonObject(" user_properties" ) {
52+ putJsonObject(" os" ) {
53+ put(" value" , SystemInfo .getOsNameAndVersion())
5154 }
52- put(" non_personalized_ads" , true )
53- putJsonArray(" events" ) {
54- addJsonObject { event.buildJsonObj(this ) }
55+ putJsonObject(" productCode" ) {
56+ put(" value" , ApplicationInfo .getInstance().build.productCode)
5557 }
56- }.toString().also { println (it) }.encodeToByteArray())
57- }
58+ putJsonObject(" productVersion" ) {
59+ put(" value" , ApplicationInfo .getInstance().build.asStringWithoutProductCodeAndSnapshot())
60+ }
61+ putJsonObject(" version" ) {
62+ // avoid PluginId.getId: it compiles to a PluginId.Companion reference that older IDEs (< 2025.2) don't have
63+ put(" value" , PluginManagerCore .plugins.find { it.pluginId.idString == R .keys.pluginId }?.version)
64+ }
65+ }
66+ put(" non_personalized_ads" , true )
67+ putJsonArray(" events" ) {
68+ addJsonObject { event.buildJsonObj(this ) }
69+ }
70+ }.toString().also { println (it) }.encodeToByteArray())
5871 }
5972 }
6073
0 commit comments