123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.server.telemetry;
-
- import com.google.common.annotations.VisibleForTesting;
- import java.time.Instant;
- import java.time.ZoneOffset;
- import java.time.format.DateTimeFormatter;
- import java.util.List;
- import java.util.Locale;
- import org.apache.commons.codec.digest.DigestUtils;
- import org.jetbrains.annotations.NotNull;
- import org.sonar.api.utils.System2;
- import org.sonar.api.utils.text.JsonWriter;
- import org.sonar.core.telemetry.TelemetryExtension;
-
- import static org.sonar.api.utils.DateUtils.DATETIME_FORMAT;
-
- public class TelemetryDataJsonWriter {
-
- @VisibleForTesting
- static final String MANAGED_INSTANCE_PROPERTY = "managedInstanceInformation";
- @VisibleForTesting
- static final String CLOUD_USAGE_PROPERTY = "cloudUsage";
-
- private static final String LANGUAGE_PROPERTY = "language";
- private static final String VERSION = "version";
- private static final String NCD_ID = "ncdId";
- private static final String PROJECT_ID = "projectUuid";
-
- private final List<TelemetryExtension> extensions;
-
- private final System2 system2;
-
- public TelemetryDataJsonWriter(List<TelemetryExtension> extensions, System2 system2) {
- this.extensions = extensions;
- this.system2 = system2;
- }
-
- public void writeTelemetryData(JsonWriter json, TelemetryData telemetryData) {
- json.beginObject();
- json.prop("id", telemetryData.getServerId());
- json.prop(VERSION, telemetryData.getVersion());
- json.prop("messageSequenceNumber", telemetryData.getMessageSequenceNumber());
- json.prop("localTimestamp", toUtc(system2.now()));
- json.prop(NCD_ID, telemetryData.getNcdId());
- telemetryData.getEdition().ifPresent(e -> json.prop("edition", e.name().toLowerCase(Locale.ENGLISH)));
- json.prop("defaultQualityGate", telemetryData.getDefaultQualityGate());
- json.prop("sonarway_quality_gate_uuid", telemetryData.getSonarWayQualityGate());
- json.name("database");
- json.beginObject();
- json.prop("name", telemetryData.getDatabase().name());
- json.prop(VERSION, telemetryData.getDatabase().version());
- json.endObject();
- json.name("plugins");
- json.beginArray();
- telemetryData.getPlugins().forEach((plugin, version) -> {
- json.beginObject();
- json.prop("name", plugin);
- json.prop(VERSION, version);
- json.endObject();
- });
- json.endArray();
-
- if (!telemetryData.getCustomSecurityConfigs().isEmpty()) {
- json.name("customSecurityConfig");
- json.beginArray();
- json.values(telemetryData.getCustomSecurityConfigs());
- json.endArray();
- }
-
- telemetryData.hasUnanalyzedC().ifPresent(hasUnanalyzedC -> json.prop("hasUnanalyzedC", hasUnanalyzedC));
- telemetryData.hasUnanalyzedCpp().ifPresent(hasUnanalyzedCpp -> json.prop("hasUnanalyzedCpp", hasUnanalyzedCpp));
-
- if (telemetryData.getInstallationDate() != null) {
- json.prop("installationDate", toUtc(telemetryData.getInstallationDate()));
- }
- if (telemetryData.getInstallationVersion() != null) {
- json.prop("installationVersion", telemetryData.getInstallationVersion());
- }
- json.prop("container", telemetryData.isInContainer());
-
- writeUserData(json, telemetryData);
- writeProjectData(json, telemetryData);
- writeProjectStatsData(json, telemetryData);
- writeBranches(json, telemetryData);
- writeNewCodeDefinitions(json, telemetryData);
- writeQualityGates(json, telemetryData);
- writeQualityProfiles(json, telemetryData);
- writeManagedInstanceInformation(json, telemetryData.getManagedInstanceInformation());
- writeCloudUsage(json, telemetryData.getCloudUsage());
- extensions.forEach(e -> e.write(json));
-
- json.endObject();
- }
-
- private static void writeUserData(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getUserTelemetries() != null) {
- json.name("users");
- json.beginArray();
- telemetryData.getUserTelemetries().forEach(user -> {
- json.beginObject();
- json.prop("userUuid", DigestUtils.sha3_224Hex(user.getUuid()));
- json.prop("status", user.isActive() ? "active" : "inactive");
- json.prop("identityProvider", user.getExternalIdentityProvider());
-
- if (user.getLastConnectionDate() != null) {
- json.prop("lastActivity", toUtc(user.getLastConnectionDate()));
- }
- if (user.getLastSonarlintConnectionDate() != null) {
- json.prop("lastSonarlintActivity", toUtc(user.getLastSonarlintConnectionDate()));
- }
- json.prop("managed", user.getScimUuid() != null);
-
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeProjectData(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getProjects() != null) {
- json.name("projects");
- json.beginArray();
- telemetryData.getProjects().forEach(project -> {
- json.beginObject();
- json.prop(PROJECT_ID, project.projectUuid());
- if (project.lastAnalysis() != null) {
- json.prop("lastAnalysis", toUtc(project.lastAnalysis()));
- }
- json.prop(LANGUAGE_PROPERTY, project.language());
- json.prop("loc", project.loc());
- json.prop("qualityProfile", project.qualityProfile());
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeBranches(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getBranches() != null) {
- json.name("branches");
- json.beginArray();
- telemetryData.getBranches().forEach(branch -> {
- json.beginObject();
- json.prop(PROJECT_ID, branch.projectUuid());
- json.prop("branchUuid", branch.branchUuid());
- json.prop(NCD_ID, branch.ncdId());
- json.prop("greenQualityGateCount", branch.greenQualityGateCount());
- json.prop("analysisCount", branch.analysisCount());
- json.prop("excludeFromPurge", branch.excludeFromPurge());
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeNewCodeDefinitions(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getNewCodeDefinitions() != null) {
- json.name("new-code-definitions");
- json.beginArray();
- telemetryData.getNewCodeDefinitions().forEach(ncd -> {
- json.beginObject();
- json.prop(NCD_ID, ncd.hashCode());
- json.prop("type", ncd.type());
- json.prop("value", ncd.value());
- json.prop("scope", ncd.scope());
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeProjectStatsData(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getProjectStatistics() != null) {
- json.name("projects-general-stats");
- json.beginArray();
- telemetryData.getProjectStatistics().forEach(project -> {
- json.beginObject();
- json.prop(PROJECT_ID, project.getProjectUuid());
- json.prop("branchCount", project.getBranchCount());
- json.prop("pullRequestCount", project.getPullRequestCount());
- json.prop("qualityGate", project.getQualityGate());
- json.prop("scm", project.getScm());
- json.prop("ci", project.getCi());
- json.prop("devopsPlatform", project.getDevopsPlatform());
- json.prop(NCD_ID, project.getNcdId());
- json.prop("project_creation_method", project.getCreationMethod().name());
- json.prop("monorepo", project.isMonorepo());
- project.getBugs().ifPresent(bugs -> json.prop("bugs", bugs));
- project.getVulnerabilities().ifPresent(vulnerabilities -> json.prop("vulnerabilities", vulnerabilities));
- project.getSecurityHotspots().ifPresent(securityHotspots -> json.prop("securityHotspots", securityHotspots));
- project.getTechnicalDebt().ifPresent(technicalDebt -> json.prop("technicalDebt", technicalDebt));
- project.getDevelopmentCost().ifPresent(developmentCost -> json.prop("developmentCost", developmentCost));
- project.getExternalSecurityReportExportedAt().ifPresent(exportedAt -> json.prop("externalSecurityReportExportedAt", exportedAt));
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeQualityGates(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getQualityGates() != null) {
- json.name("quality-gates");
- json.beginArray();
- telemetryData.getQualityGates().forEach(qualityGate -> {
- json.beginObject();
- json.prop("uuid", qualityGate.uuid());
- json.prop("caycStatus", qualityGate.caycStatus());
- json.name("conditions");
- json.beginArray();
- qualityGate.conditions().forEach(condition -> {
- json.beginObject();
- json.prop("metric", condition.getMetricKey());
- json.prop("comparison_operator", condition.getOperator().getDbValue());
- json.prop("error_value", condition.getErrorThreshold());
- json.endObject();
- });
- json.endArray();
- json.endObject();
- });
- json.endArray();
- }
- }
-
- private static void writeQualityProfiles(JsonWriter json, TelemetryData telemetryData) {
- if (telemetryData.getQualityProfiles() != null) {
- json.name("quality-profiles");
- json.beginArray();
- telemetryData.getQualityProfiles().forEach(qualityProfile -> {
- json.beginObject();
- json.prop("uuid", qualityProfile.uuid());
- json.prop("parentUuid", qualityProfile.parentUuid());
- json.prop(LANGUAGE_PROPERTY, qualityProfile.language());
- json.prop("default", qualityProfile.isDefault());
- json.prop("builtIn", qualityProfile.isBuiltIn());
- if (qualityProfile.builtInParent() != null) {
- json.prop("builtInParent", qualityProfile.builtInParent());
- }
- json.prop("rulesOverriddenCount", qualityProfile.rulesOverriddenCount());
- json.prop("rulesActivatedCount", qualityProfile.rulesActivatedCount());
- json.prop("rulesDeactivatedCount", qualityProfile.rulesDeactivatedCount());
- json.endObject();
- });
- json.endArray();
- }
- }
- private static void writeManagedInstanceInformation(JsonWriter json, TelemetryData.ManagedInstanceInformation provider) {
- json.name(MANAGED_INSTANCE_PROPERTY);
- json.beginObject();
- json.prop("isManaged", provider.isManaged());
- json.prop("provider", provider.isManaged() ? provider.provider() : null);
- json.endObject();
- }
-
- private static void writeCloudUsage(JsonWriter json, TelemetryData.CloudUsage cloudUsage) {
- json.name(CLOUD_USAGE_PROPERTY);
- json.beginObject();
- json.prop("kubernetes", cloudUsage.kubernetes());
- json.prop("kubernetesVersion", cloudUsage.kubernetesVersion());
- json.prop("kubernetesPlatform", cloudUsage.kubernetesPlatform());
- json.prop("kubernetesProvider", cloudUsage.kubernetesProvider());
- json.prop("officialHelmChart", cloudUsage.officialHelmChart());
- json.prop("containerRuntime", cloudUsage.containerRuntime());
- json.prop("officialImage", cloudUsage.officialImage());
- json.endObject();
- }
-
- @NotNull
- private static String toUtc(long date) {
- return DateTimeFormatter.ofPattern(DATETIME_FORMAT)
- .withZone(ZoneOffset.UTC)
- .format(Instant.ofEpochMilli(date));
- }
-
- }
|