Skip to content

Commit 5fdfd58

Browse files
authored
Merge pull request #2300 from digma-ai/api-performance-http-neto
api performance neto http
2 parents 6282d02 + 8e87fc3 commit 5fdfd58

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

analytics-provider/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
implementation(libs.retrofit.scalars)
3030
implementation(libs.jackson.datetime)
3131
implementation(libs.guava)
32+
implementation(libs.commons.lang3)
3233
implementation(libs.okhttp.logging) {
3334
isTransitive = false
3435
}

analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.common.io.CharStreams;
66
import okhttp3.*;
77
import okhttp3.logging.HttpLoggingInterceptor;
8+
import org.apache.commons.lang3.time.StopWatch;
89
import org.digma.intellij.plugin.model.rest.AboutResult;
910
import org.digma.intellij.plugin.model.rest.assets.AssetDisplayInfo;
1011
import org.digma.intellij.plugin.model.rest.codelens.*;
@@ -45,6 +46,8 @@
4546

4647
public class RestAnalyticsProvider implements AnalyticsProvider, Closeable {
4748

49+
public static final ThreadLocal<Long> PERFORMANCE = new ThreadLocal<>();
50+
4851
private final Client client;
4952
private final String apiUrl;
5053

@@ -498,6 +501,8 @@ public Client(String baseUrl, List<AuthenticationProvider> authenticationProvide
498501
}));
499502

500503

504+
addPerformanceInterceptor(builder);
505+
501506
//always add the logging interceptor last, so it will log info from all other interceptors
502507
addLoggingInterceptor(builder, logger);
503508

@@ -521,6 +526,20 @@ public Client(String baseUrl, List<AuthenticationProvider> authenticationProvide
521526
analyticsProvider = retrofit.create(AnalyticsProviderRetrofit.class);
522527
}
523528

529+
private void addPerformanceInterceptor(OkHttpClient.Builder builder) {
530+
builder.addInterceptor(chain -> {
531+
PERFORMANCE.remove();
532+
var stopWatch = StopWatch.createStarted();
533+
try {
534+
return chain.proceed(chain.request());
535+
} finally {
536+
stopWatch.stop();
537+
var time = stopWatch.getTime(TimeUnit.MILLISECONDS);
538+
PERFORMANCE.set(time);
539+
}
540+
});
541+
}
542+
524543

525544
private void addLoggingInterceptor(OkHttpClient.Builder builder, Consumer<String> logger) {
526545

@@ -535,6 +554,7 @@ private void addLoggingInterceptor(OkHttpClient.Builder builder, Consumer<String
535554
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
536555
if (!logSensitive) {
537556
logging.redactHeader("Authorization");
557+
logging.redactHeader("Digma-Access-Token");
538558
}
539559
builder.addInterceptor(logging);
540560
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
692692
} finally {
693693
stopWatch.stop();
694694
if (BackendConnectionMonitor.getInstance(project).isConnectionOk()) {
695-
project.getService(ApiPerformanceMonitor.class).addPerformance(method.getName(), stopWatch.getTime(TimeUnit.MILLISECONDS));
695+
//httpNetoTime may be null, usually it will not but the ThreadLocal is nullable by default so need to check
696+
var httpNetoTime = RestAnalyticsProvider.PERFORMANCE.get();
697+
if (httpNetoTime != null) {
698+
project.getService(ApiPerformanceMonitor.class).addPerformance(method.getName(), stopWatch.getTime(TimeUnit.MILLISECONDS), httpNetoTime);
699+
}
696700
}
697701
if (LOGGER.isTraceEnabled()) {
698702
Log.log(LOGGER::trace, "Api call {} took {} milliseconds", method.getName(), stopWatch.getTime(TimeUnit.MILLISECONDS));

ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/ApiPerformanceMonitor.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import com.intellij.openapi.project.Project
55
import org.digma.intellij.plugin.common.FrequencyDetector
66
import org.digma.intellij.plugin.posthog.ActivityMonitor
77
import java.util.Collections
8-
import kotlin.math.max
98
import kotlin.time.Duration.Companion.minutes
109
import kotlin.time.toJavaDuration
1110

@@ -15,7 +14,7 @@ class ApiPerformanceMonitor(private val project: Project) {
1514

1615
private val frequencyDetector = FrequencyDetector(10.minutes.toJavaDuration())
1716

18-
private val durations = Collections.synchronizedMap(mutableMapOf<String, Long>())
17+
private val durations = Collections.synchronizedMap(mutableMapOf<String, Pair<Long, Long>>())
1918

2019
//this method is not thread safe. max duration for apiName may not be the real max if two threads for
2120
// the same apiName run concurrently, and they have different durations.
@@ -24,28 +23,37 @@ class ApiPerformanceMonitor(private val project: Project) {
2423
//but locking means that threads will wait for each other.
2524
//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
2625
// worth the complexity of a coroutine.
27-
fun addPerformance(apiName: String, duration: Long) {
26+
fun addPerformance(apiName: String, duration: Long, httpNetoTime: Long) {
2827
if (duration < 2000) {
2928
return
3029
}
3130

32-
val max = max(duration, durations[apiName] ?: 0L)
33-
durations[apiName] = max
31+
val current = Pair(duration, httpNetoTime)
32+
val latest = durations[apiName] ?: Pair(0L, 0L)
33+
//put the max in the map
34+
val toReport = if (current.first > latest.first) {
35+
durations[apiName] = current
36+
current
37+
} else {
38+
durations[apiName] = latest
39+
latest
40+
}
3441

35-
report(apiName, max)
42+
report(apiName, toReport.first, toReport.second)
3643
}
3744

38-
private fun report(apiName: String, duration: Long) {
45+
private fun report(apiName: String, duration: Long, httpNetoTime: Long) {
3946
if (frequencyDetector.isTooFrequentMessage(apiName)) {
4047
return
4148
}
4249

4350
//reset the max duration before reporting, new one will be collected
44-
durations[apiName] = 0L
51+
durations[apiName] = Pair(0L, 0L)
4552

4653
val details = mutableMapOf<String, Any>(
4754
"api name" to apiName,
48-
"duration" to duration
55+
"duration" to duration,
56+
"http neto" to httpNetoTime
4957
)
5058

5159
ActivityMonitor.getInstance(project).reportApiPerformanceIssue(details)

0 commit comments

Comments
 (0)