11package org.digma.intellij.plugin.posthog
22
33import com.fasterxml.jackson.core.JsonProcessingException
4+ import com.intellij.collaboration.async.disposingScope
45import com.intellij.openapi.Disposable
56import com.intellij.openapi.application.ApplicationInfo
67import com.intellij.openapi.components.Service
78import com.intellij.openapi.project.Project
89import com.intellij.ui.jcef.JBCefApp
910import com.posthog.java.PostHog
11+ import kotlinx.coroutines.delay
12+ import kotlinx.coroutines.isActive
13+ import kotlinx.coroutines.launch
1014import kotlinx.datetime.toJavaInstant
1115import org.digma.intellij.plugin.analytics.BackendConnectionMonitor
1216import org.digma.intellij.plugin.common.ExceptionUtils
@@ -32,6 +36,7 @@ import java.time.Duration
3236import java.time.Instant
3337import java.time.temporal.ChronoUnit
3438import java.util.Date
39+ import kotlin.time.Duration.Companion.minutes
3540
3641private const val INSTALL_STATUS_PROPERTY_NAME = " install_status"
3742private const val ENVIRONMENT_ADDED_PROPERTY_NAME = " environment_added"
@@ -44,10 +49,24 @@ enum class InstallStatus { Active, Uninstalled, Disabled }
4449class ActivityMonitor (private val project : Project ) : Disposable {
4550
4651 companion object {
52+
53+ private const val TOKEN = " phc_5sy6Kuv1EYJ9GAdWPeGl7gx31RAw7BR7NHnOuLCUQZK"
54+
55+
4756 @JvmStatic
4857 fun getInstance (project : Project ): ActivityMonitor {
4958 return project.getService(ActivityMonitor ::class .java)
5059 }
60+
61+
62+ private fun checkAndConnect (): PostHog ? {
63+ return if (PosthogConnectionTester .isConnectionToPosthogOk()) {
64+ return PostHog .Builder (TOKEN ).build()
65+ } else {
66+ null
67+ }
68+ }
69+
5170 }
5271
5372
@@ -62,18 +81,55 @@ class ActivityMonitor(private val project: Project) : Disposable {
6281
6382 SessionMetadataProperties .getInstance().put(CURRENT_INSTALL_STATUS_KEY , InstallStatus .Active )
6483
65- val token = " phc_5sy6Kuv1EYJ9GAdWPeGl7gx31RAw7BR7NHnOuLCUQZK "
66- postHog = PostHog . Builder (token).build()
84+ postHog = checkAndConnect()
85+
6786 registerSessionDetails()
6887
6988 ServerVersionMonitor .getInstance(project)
7089 PluginActivityMonitor .getInstance(project)
7190
7291 settingsChangeTracker.start(this )
7392
93+ // why we need all that?
94+ // there are users that can't connect to posthog due to network restrictions like firewalls or company proxies.
95+ // these users complain that there are many errors in idea.log. that is because PostHog prints connection errors with
96+ // System.out/err and e.printStackTrace(), this is fault behaviour of posthog java client, and we can't change it.
97+ // posthog does not throw or notifies about connection errors,it just prints them and doesn't send the events.
98+ // So, on initialization, we try to connect with checkAndConnect(), if it fails then maybe it's one of these users,
99+ // and we'll probably never succeed. checkAndConnect() will return null and when posthog is null no events will be
100+ // sent and no errors will be logged.
101+ // but, it could be just a momentary network issue, that's why we keep trying every 5 minutes.
102+ // once we succeed connection and posthog is not null we stop trying because we have a posthog client.
103+ // don't care if there are network issues later, the only reason for all this is to identify users that
104+ // can never connect to posthog and prevent the unnecessary errors to idea.log.
105+
106+ // if posthog is not null don't even start the coroutine.
107+ // try to keep connecting to posthog every 5 minutes but give up after 1 hour, if we couldn't connect for
108+ // 1 hour it probably means we can't connect. this is meant to prevent endless trying when in fact there is
109+ // no way to connect, usually it will be the users mentioned above were connection to posthog is impossible.
110+ if (postHog == null ) {
111+ val startTime = Instant .now()
112+ @Suppress(" UnstableApiUsage" )
113+ disposingScope().launch {
114+ // once we have a non-null posthog stop this coroutine.
115+ // give up 1 hour after startTime.
116+ while (isActive &&
117+ postHog == null &&
118+ startTime.plus(1 , ChronoUnit .HOURS ).isAfter(Instant .now())
119+ ) {
120+ delay(5 .minutes.inWholeMilliseconds)
121+ if (isActive) {
122+ postHog = checkAndConnect()
123+ }
124+ }
125+ }
126+ }
127+
74128 }
75129
76130
131+
132+
77133 override fun dispose () {
78134 // nothing to do, used as parent disposable
79135 }
0 commit comments