Skip to content

Commit 3823b50

Browse files
committed
SONARPY-2464 Add safety around the telemetry mechanism
1 parent 8c094f8 commit 3823b50

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

sonar-python-plugin/src/main/java/org/sonar/plugins/python/SensorTelemetryStorage.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
2424
import org.sonar.api.batch.sensor.SensorContext;
25+
import org.sonar.api.utils.Version;
2526

2627
public class SensorTelemetryStorage {
2728
private static final Logger LOG = LoggerFactory.getLogger(SensorTelemetryStorage.class);
@@ -37,10 +38,22 @@ public Map<String, String> data() {
3738
}
3839

3940
public void send(SensorContext sensorContext) {
40-
data.forEach((k, v) -> {
41-
LOG.info("Metrics property: {}={}", k, v);
42-
sensorContext.addTelemetryProperty(k, v);
43-
});
41+
// This try/catch block should be useless, as in the worst case it should be a no-op depending on the SensorContext implementation
42+
// It exists to be extra sure for the LTA
43+
try {
44+
var apiVersion = sensorContext.runtime().getApiVersion();
45+
if (apiVersion.isGreaterThanOrEqual(Version.create(10, 9))) {
46+
data.forEach((k, v) -> {
47+
LOG.info("Collected metric: {}={}", k, v);
48+
sensorContext.addTelemetryProperty(k, v);
49+
});
50+
51+
} else {
52+
LOG.info("Skipping sending metrics because the plugin API version is {}", apiVersion);
53+
}
54+
} catch (Exception e) {
55+
LOG.error("Failed to send metrics", e);
56+
}
4457
}
4558

4659
public void updateMetric(MetricKey key, String value) {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.plugins.python;
18+
19+
import java.io.File;
20+
import org.junit.jupiter.api.Assertions;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.RegisterExtension;
23+
import org.slf4j.event.Level;
24+
import org.sonar.api.SonarEdition;
25+
import org.sonar.api.SonarQubeSide;
26+
import org.sonar.api.batch.sensor.SensorContext;
27+
import org.sonar.api.batch.sensor.internal.SensorContextTester;
28+
import org.sonar.api.internal.SonarRuntimeImpl;
29+
import org.sonar.api.testfixtures.log.LogTesterJUnit5;
30+
import org.sonar.api.utils.Version;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.mockito.ArgumentMatchers.any;
34+
import static org.mockito.Mockito.doThrow;
35+
import static org.mockito.Mockito.never;
36+
import static org.mockito.Mockito.spy;
37+
import static org.mockito.Mockito.verify;
38+
39+
class SensorTelemetryStorageTest {
40+
41+
@RegisterExtension
42+
public LogTesterJUnit5 logTester = new LogTesterJUnit5().setLevel(Level.DEBUG);
43+
44+
@Test
45+
void no_send_on_incompatible_version() {
46+
var sensorContext = sensorContext(Version.create(10, 8));
47+
var storage = new SensorTelemetryStorage();
48+
storage.updateMetric(SensorTelemetryStorage.MetricKey.NOTEBOOK_PRESENT_KEY, "1");
49+
storage.send(sensorContext);
50+
51+
verify(sensorContext, never()).addTelemetryProperty(any(), any());
52+
assertThat(logTester.logs()).contains("Skipping sending metrics because the plugin API version is 10.8");
53+
}
54+
55+
@Test
56+
void send_after_10_9() {
57+
var sensorContext = sensorContext(Version.create(10, 9));
58+
var storage = new SensorTelemetryStorage();
59+
storage.updateMetric(SensorTelemetryStorage.MetricKey.NOTEBOOK_PRESENT_KEY, "1");
60+
storage.send(sensorContext);
61+
62+
verify(sensorContext).addTelemetryProperty(SensorTelemetryStorage.MetricKey.NOTEBOOK_PRESENT_KEY.key(), "1");
63+
}
64+
65+
@Test
66+
void no_crash_on_exception() {
67+
var sensorContext = sensorContext(Version.create(10, 9));
68+
doThrow(new RuntimeException("Some exception")).when(sensorContext).addTelemetryProperty(any(), any());
69+
var storage = new SensorTelemetryStorage();
70+
storage.updateMetric(SensorTelemetryStorage.MetricKey.NOTEBOOK_PRESENT_KEY, "1");
71+
Assertions.assertDoesNotThrow(() -> storage.send(sensorContext));
72+
assertThat(logTester.logs()).contains("Failed to send metrics");
73+
}
74+
75+
private SensorContext sensorContext(Version version) {
76+
var sensorContext = spy(SensorContextTester.create(new File("")));
77+
sensorContext.setRuntime(SonarRuntimeImpl.forSonarQube(version, SonarQubeSide.SERVER, SonarEdition.DEVELOPER));
78+
return sensorContext;
79+
}
80+
}

0 commit comments

Comments
 (0)