Skip to content

Commit 486f337

Browse files
authored
Merge pull request #2246 from digma-ai/api-performance-monitor
api performance issue event Closes #2245
2 parents a9ddd42 + 0488c5b commit 486f337

File tree

6 files changed

+90
-10
lines changed

6 files changed

+90
-10
lines changed

ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,10 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
707707
throw e;
708708
} finally {
709709
stopWatch.stop();
710-
Log.log(LOGGER::trace, "Api call {} took {} milliseconds", method.getName(), stopWatch.getTime(TimeUnit.MILLISECONDS));
710+
project.getService(ApiPerformanceMonitor.class).addPerformance(method.getName(),stopWatch.getTime(TimeUnit.MILLISECONDS));
711+
if (LOGGER.isTraceEnabled()) {
712+
Log.log(LOGGER::trace, "Api call {} took {} milliseconds", method.getName(), stopWatch.getTime(TimeUnit.MILLISECONDS));
713+
}
711714
}
712715
}
713716

ide-common/src/main/java/org/digma/intellij/plugin/log/Log.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.intellij.openapi.diagnostic.Logger;
44
import com.intellij.openapi.project.Project;
55
import org.digma.intellij.plugin.analytics.NoSelectedEnvironmentException;
6+
import org.digma.intellij.plugin.common.FrequencyDetector;
67
import org.digma.intellij.plugin.errorreporting.*;
78

89
import java.time.Duration;
@@ -19,7 +20,7 @@ public class Log {
1920
public static final String API_LOGGER_NAME = "api.digma.org";
2021

2122

22-
private static final FrequentErrorDetector FREQUENT_ERROR_DETECTOR = new FrequentErrorDetector(Duration.ofMinutes(30));
23+
private static final FrequencyDetector FREQUENT_ERROR_DETECTOR = new FrequencyDetector(Duration.ofMinutes(30));
2324

2425

2526

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.digma.intellij.plugin.analytics
2+
3+
import com.intellij.openapi.components.Service
4+
import com.intellij.openapi.project.Project
5+
import org.digma.intellij.plugin.common.FrequencyDetector
6+
import org.digma.intellij.plugin.posthog.ActivityMonitor
7+
import java.util.Collections
8+
import kotlin.math.max
9+
import kotlin.time.Duration.Companion.minutes
10+
import kotlin.time.toJavaDuration
11+
12+
13+
@Service(Service.Level.PROJECT)
14+
class ApiPerformanceMonitor(private val project: Project) {
15+
16+
private val frequencyDetector = FrequencyDetector(10.minutes.toJavaDuration())
17+
18+
private val durations = Collections.synchronizedMap(mutableMapOf<String, Long>())
19+
20+
//this method is not thread safe. max duration for apiName may not be the real max if two threads for
21+
// the same apiName run concurrently, and they have different durations.
22+
//it's not a bug just not completely accurate reporting.
23+
//the only way to make it accurate is to lock the code that computes max and puts it in the durations map.
24+
//but locking means that threads will wait for each other.
25+
//it's possible to execute this code in a coroutine, and then It's ok to lock, but the error is minor and doesn't
26+
// worth the complexity of a coroutine.
27+
fun addPerformance(apiName: String, duration: Long) {
28+
if (duration < 2000) {
29+
return
30+
}
31+
32+
val max = max(duration, durations[apiName] ?: 0L)
33+
durations[apiName] = max
34+
35+
report(apiName, max)
36+
}
37+
38+
private fun report(apiName: String, duration: Long) {
39+
if (frequencyDetector.isTooFrequentMessage(apiName)) {
40+
return
41+
}
42+
43+
//reset the max duration before reporting, new one will be collected
44+
durations[apiName] = 0L
45+
46+
val details = mutableMapOf<String, Any>(
47+
"api name" to apiName,
48+
"duration" to duration
49+
)
50+
51+
ActivityMonitor.getInstance(project).reportApiPerformanceIssue(details)
52+
53+
}
54+
55+
56+
}

ide-common/src/main/kotlin/org/digma/intellij/plugin/errorreporting/FrequentErrorDetector.kt renamed to ide-common/src/main/kotlin/org/digma/intellij/plugin/common/FrequencyDetector.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.digma.intellij.plugin.errorreporting
1+
package org.digma.intellij.plugin.common
22

33
import com.github.benmanes.caffeine.cache.Caffeine
44
import com.intellij.openapi.diagnostic.UntraceableException
@@ -8,11 +8,20 @@ import java.util.concurrent.atomic.AtomicInteger
88
import kotlin.time.Duration
99
import kotlin.time.toKotlinDuration
1010

11-
class FrequentErrorDetector(cacheExpirationTime: java.time.Duration) {
11+
class FrequencyDetector(cacheExpirationTime: java.time.Duration) {
1212

1313
private val myCache = MyCache(cacheExpirationTime.toKotlinDuration())
1414

1515

16+
fun isTooFrequentMessage(message: String): Boolean {
17+
val counter = myCache.getOrCreate(message)
18+
val occurrences = counter.incrementAndGet()
19+
return occurrences > 1
20+
}
21+
22+
23+
24+
1625
fun isTooFrequentError(message: String, action: String): Boolean {
1726
val counter = myCache.getOrCreate(message, action)
1827
val occurrences = counter.incrementAndGet()
@@ -84,4 +93,8 @@ private class MyCache(cacheExpirationTime: Duration) {
8493
fun getOrCreate(message: String, action: String): AtomicInteger {
8594
return cache.get("$message:$action") { AtomicInteger() }
8695
}
96+
97+
fun getOrCreate(message: String): AtomicInteger {
98+
return cache.get(message) { AtomicInteger() }
99+
}
87100
}

ide-common/src/main/kotlin/org/digma/intellij/plugin/errorreporting/ErrorReporter.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import net.bytebuddy.implementation.bind.annotation.RuntimeType
1111
import net.bytebuddy.matcher.ElementMatchers
1212
import org.digma.intellij.plugin.analytics.NoSelectedEnvironmentException
1313
import org.digma.intellij.plugin.common.ExceptionUtils
14+
import org.digma.intellij.plugin.common.FrequencyDetector
1415
import org.digma.intellij.plugin.common.findActiveProject
1516
import org.digma.intellij.plugin.common.isProjectValid
1617
import org.digma.intellij.plugin.log.Log
@@ -29,7 +30,7 @@ open class ErrorReporter {
2930

3031
private val logger: Logger = Logger.getInstance(this::class.java)
3132

32-
private val frequentErrorDetector = FrequentErrorDetector(60.minutes.toJavaDuration())
33+
private val frequencyDetector = FrequencyDetector(60.minutes.toJavaDuration())
3334

3435
//must be public class
3536
class MyPauseInterceptor {
@@ -119,7 +120,7 @@ open class ErrorReporter {
119120
open fun reportError(project: Project?, message: String, action: String, details: Map<String, String>) {
120121

121122

122-
if (frequentErrorDetector.isTooFrequentError(message, action)) {
123+
if (frequencyDetector.isTooFrequentError(message, action)) {
123124
return
124125
}
125126

@@ -150,7 +151,7 @@ open class ErrorReporter {
150151
return
151152
}
152153

153-
if (frequentErrorDetector.isTooFrequentException(message, throwable)) {
154+
if (frequencyDetector.isTooFrequentException(message, throwable)) {
154155
return
155156
}
156157

@@ -187,7 +188,7 @@ open class ErrorReporter {
187188
) {
188189

189190
try {
190-
if (frequentErrorDetector.isTooFrequentException(message, exception)) {
191+
if (frequencyDetector.isTooFrequentException(message, exception)) {
191192
return
192193
}
193194

@@ -208,7 +209,7 @@ open class ErrorReporter {
208209
}
209210

210211
open fun reportBackendError(project: Project?, message: String, action: String) {
211-
if (frequentErrorDetector.isTooFrequentError(message, action)) {
212+
if (frequencyDetector.isTooFrequentError(message, action)) {
212213
return
213214
}
214215

@@ -234,7 +235,7 @@ open class ErrorReporter {
234235
// if the error is not a result of an exception create a new RuntimeException and send it, so we have the stack trace.
235236
open fun reportInternalFatalError(project: Project, source: String, exception: Throwable, details: Map<String, String> = mapOf()) {
236237

237-
if (frequentErrorDetector.isTooFrequentException(source, exception)) {
238+
if (frequencyDetector.isTooFrequentException(source, exception)) {
238239
return
239240
}
240241

ide-common/src/main/kotlin/org/digma/intellij/plugin/posthog/ActivityMonitor.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,5 +989,11 @@ class ActivityMonitor(private val project: Project) : Disposable {
989989
return PersistenceService.getInstance().getUserRegistrationEmail() ?: PersistenceService.getInstance().getUserEmail() ?: ""
990990
}
991991

992+
fun reportApiPerformanceIssue(details: MutableMap<String, Any>) {
993+
capture(
994+
"api-performance-issue",details
995+
)
996+
}
997+
992998

993999
}

0 commit comments

Comments
 (0)