You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TelemetryDataJsonWriter.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-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 GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.telemetry;
  21. import com.google.common.annotations.VisibleForTesting;
  22. import java.time.Instant;
  23. import java.time.ZoneOffset;
  24. import java.time.format.DateTimeFormatter;
  25. import java.util.List;
  26. import java.util.Locale;
  27. import org.apache.commons.codec.digest.DigestUtils;
  28. import org.jetbrains.annotations.NotNull;
  29. import org.sonar.api.utils.System2;
  30. import org.sonar.api.utils.text.JsonWriter;
  31. import org.sonar.core.telemetry.TelemetryExtension;
  32. import static org.sonar.api.utils.DateUtils.DATETIME_FORMAT;
  33. public class TelemetryDataJsonWriter {
  34. @VisibleForTesting
  35. static final String MANAGED_INSTANCE_PROPERTY = "managedInstanceInformation";
  36. @VisibleForTesting
  37. static final String CLOUD_USAGE_PROPERTY = "cloudUsage";
  38. private static final String LANGUAGE_PROPERTY = "language";
  39. private static final String VERSION = "version";
  40. private static final String NCD_ID = "ncdId";
  41. private static final String PROJECT_ID = "projectUuid";
  42. private final List<TelemetryExtension> extensions;
  43. private final System2 system2;
  44. public TelemetryDataJsonWriter(List<TelemetryExtension> extensions, System2 system2) {
  45. this.extensions = extensions;
  46. this.system2 = system2;
  47. }
  48. public void writeTelemetryData(JsonWriter json, TelemetryData telemetryData) {
  49. json.beginObject();
  50. json.prop("id", telemetryData.getServerId());
  51. json.prop(VERSION, telemetryData.getVersion());
  52. json.prop("messageSequenceNumber", telemetryData.getMessageSequenceNumber());
  53. json.prop("localTimestamp", toUtc(system2.now()));
  54. json.prop(NCD_ID, telemetryData.getNcdId());
  55. telemetryData.getEdition().ifPresent(e -> json.prop("edition", e.name().toLowerCase(Locale.ENGLISH)));
  56. json.prop("defaultQualityGate", telemetryData.getDefaultQualityGate());
  57. json.prop("sonarway_quality_gate_uuid", telemetryData.getSonarWayQualityGate());
  58. json.name("database");
  59. json.beginObject();
  60. json.prop("name", telemetryData.getDatabase().name());
  61. json.prop(VERSION, telemetryData.getDatabase().version());
  62. json.endObject();
  63. json.name("plugins");
  64. json.beginArray();
  65. telemetryData.getPlugins().forEach((plugin, version) -> {
  66. json.beginObject();
  67. json.prop("name", plugin);
  68. json.prop(VERSION, version);
  69. json.endObject();
  70. });
  71. json.endArray();
  72. if (!telemetryData.getCustomSecurityConfigs().isEmpty()) {
  73. json.name("customSecurityConfig");
  74. json.beginArray();
  75. json.values(telemetryData.getCustomSecurityConfigs());
  76. json.endArray();
  77. }
  78. telemetryData.hasUnanalyzedC().ifPresent(hasUnanalyzedC -> json.prop("hasUnanalyzedC", hasUnanalyzedC));
  79. telemetryData.hasUnanalyzedCpp().ifPresent(hasUnanalyzedCpp -> json.prop("hasUnanalyzedCpp", hasUnanalyzedCpp));
  80. if (telemetryData.getInstallationDate() != null) {
  81. json.prop("installationDate", toUtc(telemetryData.getInstallationDate()));
  82. }
  83. if (telemetryData.getInstallationVersion() != null) {
  84. json.prop("installationVersion", telemetryData.getInstallationVersion());
  85. }
  86. json.prop("container", telemetryData.isInContainer());
  87. writeUserData(json, telemetryData);
  88. writeProjectData(json, telemetryData);
  89. writeProjectStatsData(json, telemetryData);
  90. writeBranches(json, telemetryData);
  91. writeNewCodeDefinitions(json, telemetryData);
  92. writeQualityGates(json, telemetryData);
  93. writeQualityProfiles(json, telemetryData);
  94. writeManagedInstanceInformation(json, telemetryData.getManagedInstanceInformation());
  95. writeCloudUsage(json, telemetryData.getCloudUsage());
  96. extensions.forEach(e -> e.write(json));
  97. json.endObject();
  98. }
  99. private static void writeUserData(JsonWriter json, TelemetryData telemetryData) {
  100. if (telemetryData.getUserTelemetries() != null) {
  101. json.name("users");
  102. json.beginArray();
  103. telemetryData.getUserTelemetries().forEach(user -> {
  104. json.beginObject();
  105. json.prop("userUuid", DigestUtils.sha3_224Hex(user.getUuid()));
  106. json.prop("status", user.isActive() ? "active" : "inactive");
  107. json.prop("identityProvider", user.getExternalIdentityProvider());
  108. if (user.getLastConnectionDate() != null) {
  109. json.prop("lastActivity", toUtc(user.getLastConnectionDate()));
  110. }
  111. if (user.getLastSonarlintConnectionDate() != null) {
  112. json.prop("lastSonarlintActivity", toUtc(user.getLastSonarlintConnectionDate()));
  113. }
  114. json.prop("managed", user.getScimUuid() != null);
  115. json.endObject();
  116. });
  117. json.endArray();
  118. }
  119. }
  120. private static void writeProjectData(JsonWriter json, TelemetryData telemetryData) {
  121. if (telemetryData.getProjects() != null) {
  122. json.name("projects");
  123. json.beginArray();
  124. telemetryData.getProjects().forEach(project -> {
  125. json.beginObject();
  126. json.prop(PROJECT_ID, project.projectUuid());
  127. if (project.lastAnalysis() != null) {
  128. json.prop("lastAnalysis", toUtc(project.lastAnalysis()));
  129. }
  130. json.prop(LANGUAGE_PROPERTY, project.language());
  131. json.prop("loc", project.loc());
  132. json.prop("qualityProfile", project.qualityProfile());
  133. json.endObject();
  134. });
  135. json.endArray();
  136. }
  137. }
  138. private static void writeBranches(JsonWriter json, TelemetryData telemetryData) {
  139. if (telemetryData.getBranches() != null) {
  140. json.name("branches");
  141. json.beginArray();
  142. telemetryData.getBranches().forEach(branch -> {
  143. json.beginObject();
  144. json.prop(PROJECT_ID, branch.projectUuid());
  145. json.prop("branchUuid", branch.branchUuid());
  146. json.prop(NCD_ID, branch.ncdId());
  147. json.prop("greenQualityGateCount", branch.greenQualityGateCount());
  148. json.prop("analysisCount", branch.analysisCount());
  149. json.prop("excludeFromPurge", branch.excludeFromPurge());
  150. json.endObject();
  151. });
  152. json.endArray();
  153. }
  154. }
  155. private static void writeNewCodeDefinitions(JsonWriter json, TelemetryData telemetryData) {
  156. if (telemetryData.getNewCodeDefinitions() != null) {
  157. json.name("new-code-definitions");
  158. json.beginArray();
  159. telemetryData.getNewCodeDefinitions().forEach(ncd -> {
  160. json.beginObject();
  161. json.prop(NCD_ID, ncd.hashCode());
  162. json.prop("type", ncd.type());
  163. json.prop("value", ncd.value());
  164. json.prop("scope", ncd.scope());
  165. json.endObject();
  166. });
  167. json.endArray();
  168. }
  169. }
  170. private static void writeProjectStatsData(JsonWriter json, TelemetryData telemetryData) {
  171. if (telemetryData.getProjectStatistics() != null) {
  172. json.name("projects-general-stats");
  173. json.beginArray();
  174. telemetryData.getProjectStatistics().forEach(project -> {
  175. json.beginObject();
  176. json.prop(PROJECT_ID, project.getProjectUuid());
  177. json.prop("branchCount", project.getBranchCount());
  178. json.prop("pullRequestCount", project.getPullRequestCount());
  179. json.prop("qualityGate", project.getQualityGate());
  180. json.prop("scm", project.getScm());
  181. json.prop("ci", project.getCi());
  182. json.prop("devopsPlatform", project.getDevopsPlatform());
  183. json.prop(NCD_ID, project.getNcdId());
  184. json.prop("project_creation_method", project.getCreationMethod().name());
  185. json.prop("monorepo", project.isMonorepo());
  186. project.getBugs().ifPresent(bugs -> json.prop("bugs", bugs));
  187. project.getVulnerabilities().ifPresent(vulnerabilities -> json.prop("vulnerabilities", vulnerabilities));
  188. project.getSecurityHotspots().ifPresent(securityHotspots -> json.prop("securityHotspots", securityHotspots));
  189. project.getTechnicalDebt().ifPresent(technicalDebt -> json.prop("technicalDebt", technicalDebt));
  190. project.getDevelopmentCost().ifPresent(developmentCost -> json.prop("developmentCost", developmentCost));
  191. project.getExternalSecurityReportExportedAt().ifPresent(exportedAt -> json.prop("externalSecurityReportExportedAt", exportedAt));
  192. json.endObject();
  193. });
  194. json.endArray();
  195. }
  196. }
  197. private static void writeQualityGates(JsonWriter json, TelemetryData telemetryData) {
  198. if (telemetryData.getQualityGates() != null) {
  199. json.name("quality-gates");
  200. json.beginArray();
  201. telemetryData.getQualityGates().forEach(qualityGate -> {
  202. json.beginObject();
  203. json.prop("uuid", qualityGate.uuid());
  204. json.prop("caycStatus", qualityGate.caycStatus());
  205. json.name("conditions");
  206. json.beginArray();
  207. qualityGate.conditions().forEach(condition -> {
  208. json.beginObject();
  209. json.prop("metric", condition.getMetricKey());
  210. json.prop("comparison_operator", condition.getOperator().getDbValue());
  211. json.prop("error_value", condition.getErrorThreshold());
  212. json.endObject();
  213. });
  214. json.endArray();
  215. json.endObject();
  216. });
  217. json.endArray();
  218. }
  219. }
  220. private static void writeQualityProfiles(JsonWriter json, TelemetryData telemetryData) {
  221. if (telemetryData.getQualityProfiles() != null) {
  222. json.name("quality-profiles");
  223. json.beginArray();
  224. telemetryData.getQualityProfiles().forEach(qualityProfile -> {
  225. json.beginObject();
  226. json.prop("uuid", qualityProfile.uuid());
  227. json.prop("parentUuid", qualityProfile.parentUuid());
  228. json.prop(LANGUAGE_PROPERTY, qualityProfile.language());
  229. json.prop("default", qualityProfile.isDefault());
  230. json.prop("builtIn", qualityProfile.isBuiltIn());
  231. if (qualityProfile.builtInParent() != null) {
  232. json.prop("builtInParent", qualityProfile.builtInParent());
  233. }
  234. json.prop("rulesOverriddenCount", qualityProfile.rulesOverriddenCount());
  235. json.prop("rulesActivatedCount", qualityProfile.rulesActivatedCount());
  236. json.prop("rulesDeactivatedCount", qualityProfile.rulesDeactivatedCount());
  237. json.endObject();
  238. });
  239. json.endArray();
  240. }
  241. }
  242. private static void writeManagedInstanceInformation(JsonWriter json, TelemetryData.ManagedInstanceInformation provider) {
  243. json.name(MANAGED_INSTANCE_PROPERTY);
  244. json.beginObject();
  245. json.prop("isManaged", provider.isManaged());
  246. json.prop("provider", provider.isManaged() ? provider.provider() : null);
  247. json.endObject();
  248. }
  249. private static void writeCloudUsage(JsonWriter json, TelemetryData.CloudUsage cloudUsage) {
  250. json.name(CLOUD_USAGE_PROPERTY);
  251. json.beginObject();
  252. json.prop("kubernetes", cloudUsage.kubernetes());
  253. json.prop("kubernetesVersion", cloudUsage.kubernetesVersion());
  254. json.prop("kubernetesPlatform", cloudUsage.kubernetesPlatform());
  255. json.prop("kubernetesProvider", cloudUsage.kubernetesProvider());
  256. json.prop("officialHelmChart", cloudUsage.officialHelmChart());
  257. json.prop("containerRuntime", cloudUsage.containerRuntime());
  258. json.prop("officialImage", cloudUsage.officialImage());
  259. json.endObject();
  260. }
  261. @NotNull
  262. private static String toUtc(long date) {
  263. return DateTimeFormatter.ofPattern(DATETIME_FORMAT)
  264. .withZone(ZoneOffset.UTC)
  265. .format(Instant.ofEpochMilli(date));
  266. }
  267. }