Skip to content

Commit 2a7386a

Browse files
authored
Feature/link to span percentiles (#110)
1 parent 12c8883 commit 2a7386a

File tree

8 files changed

+132
-15
lines changed

8 files changed

+132
-15
lines changed

analytics-provider/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies {
88
implementation(project(":model"))
99
implementation(libs.retrofit.client)
1010
implementation(libs.retrofit.jackson)
11+
implementation(libs.guava)
1112
}
1213

1314

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.digma.intellij.plugin.model.rest.errors.CodeObjectError;
55
import org.digma.intellij.plugin.model.rest.insights.CodeObjectInsight;
66
import org.digma.intellij.plugin.model.rest.insights.InsightsRequest;
7+
import org.digma.intellij.plugin.model.rest.insights.SpanHistogramQuery;
78
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummary;
89
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummaryRequest;
910
import org.digma.intellij.plugin.model.rest.usage.UsageStatusRequest;
@@ -26,4 +27,5 @@ public interface AnalyticsProvider extends Closeable {
2627

2728
UsageStatusResult getUsageStatus(UsageStatusRequest usageStatusRequest);
2829

30+
String getHtmlGraphForSpanPercentiles(SpanHistogramQuery request);
2931
}

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

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package org.digma.intellij.plugin.analytics;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.google.common.io.CharStreams;
45
import okhttp3.OkHttpClient;
56
import okhttp3.Request;
7+
import okhttp3.ResponseBody;
68
import org.digma.intellij.plugin.model.rest.errordetails.CodeObjectErrorDetails;
79
import org.digma.intellij.plugin.model.rest.errors.CodeObjectError;
810
import org.digma.intellij.plugin.model.rest.insights.CodeObjectInsight;
911
import org.digma.intellij.plugin.model.rest.insights.InsightsRequest;
12+
import org.digma.intellij.plugin.model.rest.insights.SpanHistogramQuery;
1013
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummary;
1114
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummaryRequest;
1215
import org.digma.intellij.plugin.model.rest.usage.UsageStatusRequest;
@@ -15,11 +18,24 @@
1518
import retrofit2.Response;
1619
import retrofit2.Retrofit;
1720
import retrofit2.converter.jackson.JacksonConverterFactory;
18-
import retrofit2.http.*;
19-
20-
import javax.net.ssl.*;
21+
import retrofit2.http.Body;
22+
import retrofit2.http.GET;
23+
import retrofit2.http.Headers;
24+
import retrofit2.http.POST;
25+
import retrofit2.http.Path;
26+
import retrofit2.http.Query;
27+
import retrofit2.http.Streaming;
28+
29+
import javax.net.ssl.HostnameVerifier;
30+
import javax.net.ssl.SSLContext;
31+
import javax.net.ssl.SSLSession;
32+
import javax.net.ssl.SSLSocketFactory;
33+
import javax.net.ssl.TrustManager;
34+
import javax.net.ssl.X509TrustManager;
2135
import java.io.Closeable;
2236
import java.io.IOException;
37+
import java.io.Reader;
38+
import java.io.UncheckedIOException;
2339
import java.security.KeyManagementException;
2440
import java.security.NoSuchAlgorithmException;
2541
import java.security.SecureRandom;
@@ -71,6 +87,20 @@ public UsageStatusResult getUsageStatus(UsageStatusRequest usageStatusRequest) {
7187
return execute(() -> client.analyticsProvider.getUsageStatus(usageStatusRequest));
7288
}
7389

90+
@Override
91+
public String getHtmlGraphForSpanPercentiles(SpanHistogramQuery request) {
92+
final ResponseBody responseBody = execute(() -> client.analyticsProvider.getHtmlGraphForSpanPercentiles(request));
93+
return readEntire(responseBody);
94+
}
95+
96+
protected static String readEntire(ResponseBody responseBody) {
97+
try (Reader reader = responseBody.charStream()) {
98+
return CharStreams.toString(reader);
99+
} catch (IOException e) {
100+
throw new UncheckedIOException("failed to read HTTP Response Body", e);
101+
}
102+
}
103+
74104
public <T> T execute(Supplier<Call<T>> supplier) {
75105

76106
Response<T> response;
@@ -262,6 +292,16 @@ private interface AnalyticsProviderRetrofit {
262292
@POST("/CodeAnalytics/codeobjects/status")
263293
Call<UsageStatusResult> getUsageStatus(@Body UsageStatusRequest usageStatusRequest);
264294

295+
296+
@Headers({
297+
"Accept: application/+json",
298+
"Content-Type:application/json"
299+
})
300+
@POST("/Graphs/graphForSpanPercentiles")
301+
// @Streaming means ResponseBody as is, without conversion
302+
@Streaming
303+
Call<ResponseBody> getHtmlGraphForSpanPercentiles(@Body SpanHistogramQuery request);
304+
265305
}
266306

267307
}

analytics-provider/src/test/java/org/digma/intellij/plugin/analytics/InsightsTests.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,21 @@
33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
55
import okhttp3.mockwebserver.MockResponse;
6-
import org.digma.intellij.plugin.model.rest.insights.*;
6+
import org.digma.intellij.plugin.model.rest.insights.CodeObjectInsight;
7+
import org.digma.intellij.plugin.model.rest.insights.Duration;
8+
import org.digma.intellij.plugin.model.rest.insights.ErrorInsight;
9+
import org.digma.intellij.plugin.model.rest.insights.ErrorInsightNamedError;
10+
import org.digma.intellij.plugin.model.rest.insights.HighUsageInsight;
11+
import org.digma.intellij.plugin.model.rest.insights.HotspotInsight;
12+
import org.digma.intellij.plugin.model.rest.insights.InsightsRequest;
13+
import org.digma.intellij.plugin.model.rest.insights.LowUsageInsight;
14+
import org.digma.intellij.plugin.model.rest.insights.NormalUsageInsight;
15+
import org.digma.intellij.plugin.model.rest.insights.Percentile;
16+
import org.digma.intellij.plugin.model.rest.insights.SlowEndpointInsight;
17+
import org.digma.intellij.plugin.model.rest.insights.SlowSpanInfo;
18+
import org.digma.intellij.plugin.model.rest.insights.SlowestSpansInsight;
19+
import org.digma.intellij.plugin.model.rest.insights.SpanHistogramQuery;
20+
import org.digma.intellij.plugin.model.rest.insights.SpanInfo;
721
import org.junit.jupiter.api.Test;
822

923
import java.util.ArrayList;
@@ -18,7 +32,7 @@ public class InsightsTests extends AbstractAnalyticsProviderTest {
1832

1933
//run against running env just for local test
2034
// @Test
21-
public void getInsightsTemp() {
35+
public void actualGetInsightsTemp() {
2236
{
2337
List<String> ids = new ArrayList<>();
2438
ids.add("method:Sample.MoneyTransfer.API.Domain.Services.MoneyTransferDomainService$_$TransferFunds");
@@ -34,7 +48,7 @@ public void getInsightsTemp() {
3448

3549

3650
// @Test
37-
public void getInsightsTempViaSsl() {
51+
public void actualGetInsightsTempViaSsl() {
3852
{
3953
List<String> ids = new ArrayList<>();
4054
ids.add("method:Sample.MoneyTransfer.API.Controllers.TransferController$_$TransferFunds");
@@ -47,6 +61,16 @@ public void getInsightsTempViaSsl() {
4761
}
4862
}
4963

64+
//@Test
65+
public void actualgetHtmlGraphForSpanPercentiles() {
66+
final SpanHistogramQuery query = new SpanHistogramQuery(
67+
"ARIK-LAPTOP[LOCAL]", "SampleInsights/Error", "OpenTelemetry.Instrumentation.AspNetCore", "");
68+
AnalyticsProvider analyticsProvider = new RestAnalyticsProvider("https://localhost:5051");
69+
70+
String htmlBody = analyticsProvider.getHtmlGraphForSpanPercentiles(query);
71+
72+
System.out.println("htmlBody:" + htmlBody);
73+
}
5074

5175
@Test
5276
public void getInsights() throws JsonProcessingException {

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.digma.intellij.plugin.model.rest.errors.CodeObjectError;
1212
import org.digma.intellij.plugin.model.rest.insights.CodeObjectInsight;
1313
import org.digma.intellij.plugin.model.rest.insights.InsightsRequest;
14+
import org.digma.intellij.plugin.model.rest.insights.SpanHistogramQuery;
1415
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummary;
1516
import org.digma.intellij.plugin.model.rest.summary.CodeObjectSummaryRequest;
1617
import org.digma.intellij.plugin.model.rest.usage.UsageStatusRequest;
@@ -29,7 +30,12 @@
2930
import java.lang.reflect.InvocationTargetException;
3031
import java.lang.reflect.Method;
3132
import java.net.ConnectException;
32-
import java.util.*;
33+
import java.util.ArrayList;
34+
import java.util.Arrays;
35+
import java.util.HashMap;
36+
import java.util.List;
37+
import java.util.Map;
38+
import java.util.Objects;
3339
import java.util.stream.Collectors;
3440

3541
public class AnalyticsService implements Disposable {
@@ -161,6 +167,11 @@ public UsageStatusResult getUsageStatusOfErrors(List<String> objectIds) throws A
161167
return analyticsProviderProxy.getUsageStatus(new UsageStatusRequest(objectIds, List.of("Error")));
162168
}
163169

170+
public String getHtmlGraphForSpanPercentiles(String instrumentationLibrary, String spanName) throws AnalyticsServiceException {
171+
final SpanHistogramQuery spanHistogramQuery = new SpanHistogramQuery(getCurrentEnvironment(), spanName, instrumentationLibrary, "");
172+
return analyticsProviderProxy.getHtmlGraphForSpanPercentiles(spanHistogramQuery);
173+
}
174+
164175
@Override
165176
public void dispose() {
166177
try {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.digma.intellij.plugin.model.rest.insights
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator
4+
5+
data class SpanHistogramQuery
6+
@JsonCreator
7+
constructor(
8+
val environment: String,
9+
val spanName: String,
10+
val instrumentationLibrary: String,
11+
val codeObjectId: String,
12+
)

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencyResolutionManagement {
3333
version("junit", "5.8.2")
3434
version("kotlin-stdlib", "1.6.20")
3535
library("kotlin-stdlib-jdk8", "org.jetbrains.kotlin", "kotlin-stdlib-jdk8").versionRef("kotlin-stdlib")
36+
library("guava", "com.google.guava", "guava").version("31.1-jre")
3637
version("retrofit", "2.9.0")
3738
library("retrofit-client", "com.squareup.retrofit2", "retrofit").versionRef("retrofit")
3839
library("retrofit-jackson","com.squareup.retrofit2","converter-jackson").versionRef("retrofit")

src/main/kotlin/org/digma/intellij/plugin/ui/list/insights/SpanPanels.kt

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package org.digma.intellij.plugin.ui.list.insights
22

3+
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider
34
import com.intellij.openapi.project.Project
5+
import com.intellij.ui.components.ActionLink
46
import com.intellij.ui.components.JBLabel
57
import com.intellij.ui.components.JBPanel
68
import com.intellij.ui.dsl.builder.panel
79
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
810
import com.intellij.util.containers.isNullOrEmpty
911
import com.intellij.util.ui.JBUI.Borders.empty
12+
import org.digma.intellij.plugin.analytics.AnalyticsService
1013
import org.digma.intellij.plugin.model.rest.insights.SpanDurationsInsight
1114
import org.digma.intellij.plugin.model.rest.insights.SpanDurationsPercentile
1215
import org.digma.intellij.plugin.model.rest.insights.SpanFlow
16+
import org.digma.intellij.plugin.model.rest.insights.SpanInfo
1317
import org.digma.intellij.plugin.model.rest.insights.SpanInsight
1418
import org.digma.intellij.plugin.settings.SettingsState
1519
import org.digma.intellij.plugin.ui.common.CopyableLabel
@@ -30,6 +34,7 @@ import java.awt.GridLayout
3034
import java.sql.Timestamp
3135
import java.util.Locale
3236
import java.util.concurrent.TimeUnit
37+
import javax.swing.JButton
3338
import javax.swing.JLabel
3439
import javax.swing.JPanel
3540
import javax.swing.SwingConstants
@@ -125,15 +130,15 @@ fun spanDurationPanel(
125130
}
126131
val h = ps.height
127132
val w = ps.width
128-
addCurrentLargestWidthDurationPLabel(panelsLayoutHelper,w)
129-
return Dimension(getCurrentLargestWidthDurationPLabel(panelsLayoutHelper,w), h)
133+
addCurrentLargestWidthDurationPLabel(panelsLayoutHelper, w)
134+
return Dimension(getCurrentLargestWidthDurationPLabel(panelsLayoutHelper, w), h)
130135
}
131136
}
132137
pLabelPanel.layout = BorderLayout()
133138
pLabelPanel.border = empty()
134139
pLabelPanel.isOpaque = false
135140
pLabelPanel.add(pLabel, BorderLayout.WEST)
136-
addCurrentLargestWidthDurationPLabel(panelsLayoutHelper,pLabelPanel.preferredSize.width)
141+
addCurrentLargestWidthDurationPLabel(panelsLayoutHelper, pLabelPanel.preferredSize.width)
137142
durationsPanel.add(pLabelPanel, BorderLayout.WEST)
138143

139144

@@ -162,16 +167,17 @@ fun spanDurationPanel(
162167
evalPanel.layout = BorderLayout()
163168
evalPanel.add(evalLabel, BorderLayout.CENTER)
164169
evalPanel.isOpaque = false
165-
addCurrentLargestWidthIconPanel(panelsLayoutHelper,evalPanel.preferredSize.width)
170+
addCurrentLargestWidthIconPanel(panelsLayoutHelper, evalPanel.preferredSize.width)
166171
durationsPanel.add(evalPanel, BorderLayout.EAST)
167172
}
168173

169174
durationsListPanel.add(durationsPanel)
170175

171176
}
172177

178+
val buttonToGraph = buildButtonToPercentilesGraph(project, spanDurationsInsight.span)
173179
val settingsState = SettingsState.getInstance(project)
174-
val iconPanel = buildIconPanelWithLinks(settingsState, traceSamples)
180+
val iconPanel = buildIconPanelWithLinks(settingsState, traceSamples, buttonToGraph)
175181

176182
val result = JBPanel<JBPanel<*>>()
177183
result.layout = BorderLayout()
@@ -182,13 +188,19 @@ fun spanDurationPanel(
182188
return insightItemPanel(result)
183189
}
184190

185-
fun buildIconPanelWithLinks(settingsState: SettingsState, traceSamples: List<TraceSample>): JBPanel<*> {
191+
fun buildIconPanelWithLinks(
192+
settingsState: SettingsState, traceSamples: List<TraceSample>, buttonToPercentilesGraph: JButton
193+
): JBPanel<*> {
194+
186195
val jaegerUrl = buildLinkToJaeger(settingsState, traceSamples)
187196
val iconPanel = panel {
188197
row {
189198
icon(Laf.Icons.Insight.HISTOGRAM)
190199
.horizontalAlign(HorizontalAlign.CENTER)
191200
}
201+
row {
202+
cell(buttonToPercentilesGraph)
203+
}
192204
if (jaegerUrl.isNotBlank()) {
193205
row {
194206
val bl = browserLink("Compare", jaegerUrl)
@@ -200,7 +212,10 @@ fun buildIconPanelWithLinks(settingsState: SettingsState, traceSamples: List<Tra
200212
return iconPanel
201213
}
202214

203-
fun buildLinkToJaeger(settingsState: SettingsState, traceSamples: List<TraceSample>, embedLinks: Boolean = false): String {
215+
fun buildLinkToJaeger(
216+
settingsState: SettingsState, traceSamples: List<TraceSample>, embedLinks: Boolean = false
217+
): String {
218+
204219
val jaegerBaseUrl = settingsState.jaegerUrl?.trim()?.trimEnd('/')
205220
if (jaegerBaseUrl.isNullOrBlank() || traceSamples.isNullOrEmpty()) {
206221
return ""
@@ -221,6 +236,17 @@ fun buildLinkToJaeger(settingsState: SettingsState, traceSamples: List<TraceSamp
221236
return "${jaegerBaseUrl}/trace/${trace1}...${trace2}?cohort=${trace1}&cohort=${trace2}${embedPart}"
222237
}
223238

239+
fun buildButtonToPercentilesGraph(project: Project, span: SpanInfo): ActionLink {
240+
val analyticsService = AnalyticsService.getInstance(project)
241+
val button = ActionLink("Histogram")
242+
button.addActionListener {
243+
val htmlContent = analyticsService.getHtmlGraphForSpanPercentiles(span.instrumentationLibrary, span.name)
244+
HTMLEditorProvider.openEditor(project, "Percentiles Graph of Span ${span.name}", htmlContent)
245+
}
246+
247+
return button
248+
}
249+
224250
fun buildTraceSample(percentile: SpanDurationsPercentile): TraceSample {
225251
val percentileName = "P${(percentile.percentile * 100).toInt()}"
226252
var traceId = ""
@@ -261,7 +287,7 @@ private fun getCurrentLargestWidthDurationPLabel(layoutHelper: PanelsLayoutHelpe
261287
return max(width, currentLargest)
262288
}
263289

264-
private fun addCurrentLargestWidthDurationPLabel(layoutHelper: PanelsLayoutHelper,width: Int) {
290+
private fun addCurrentLargestWidthDurationPLabel(layoutHelper: PanelsLayoutHelper, width: Int) {
265291
//this method should never throw NPE
266292
val currentLargest: Int =
267293
(layoutHelper.getObjectAttribute("SpanDurationsDurationPLabel", "largestWidth") ?: 0) as Int

0 commit comments

Comments
 (0)