diff --git a/extensions/telemetry/main.ts b/extensions/telemetry/main.ts index f0d231316d..aef51ff647 100644 --- a/extensions/telemetry/main.ts +++ b/extensions/telemetry/main.ts @@ -7,6 +7,7 @@ export default class TelemetryMainExtension extends LensMainExtension { async onActivate() { console.log("telemetry main extension activated") tracker.start() + tracker.reportPeriodically() await telemetryPreferencesStore.loadExtension(this) } diff --git a/extensions/telemetry/package-lock.json b/extensions/telemetry/package-lock.json index f0d291c1b3..57e74dc08b 100644 --- a/extensions/telemetry/package-lock.json +++ b/extensions/telemetry/package-lock.json @@ -8,6 +8,16 @@ "version": "file:../../src/extensions/npm/extensions", "dev": true }, + "@segment/loosely-validate-event": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", + "integrity": "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==", + "dev": true, + "requires": { + "component-type": "^1.2.1", + "join-component": "^1.1.0" + } + }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -225,6 +235,22 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true }, + "analytics-node": { + "version": "3.4.0-beta.3", + "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-3.4.0-beta.3.tgz", + "integrity": "sha512-NIdpxiwlZ4cKgs9MDlDe89b5bg/pMq2W7XTA+cjzCM66IwW3ujZhVE49vk+zG6Yrxk0s/DXmennJ+cCQIsTKMA==", + "dev": true, + "requires": { + "@segment/loosely-validate-event": "^2.0.0", + "axios": "^0.19.2", + "axios-retry": "^3.0.2", + "lodash.isstring": "^4.0.1", + "md5": "^2.2.1", + "ms": "^2.0.0", + "remove-trailing-slash": "^0.1.0", + "uuid": "^3.2.1" + } + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -374,6 +400,24 @@ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10" + } + }, + "axios-retry": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.1.9.tgz", + "integrity": "sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA==", + "dev": true, + "requires": { + "is-retry-allowed": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -696,6 +740,12 @@ "supports-color": "^5.3.0" } }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true + }, "chokidar": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", @@ -813,6 +863,12 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "component-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", + "integrity": "sha1-ikeQFwAjjk/DIml3EjAibyS0Fak=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -914,6 +970,12 @@ "sha.js": "^2.4.8" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -1377,6 +1439,26 @@ "readable-stream": "^2.3.6" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1784,6 +1866,12 @@ "isobject": "^3.0.1" } }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -1820,6 +1908,12 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "join-component": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", + "integrity": "sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU=", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1910,6 +2004,12 @@ "path-exists": "^3.0.0" } }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1961,6 +2061,17 @@ "object-visit": "^1.0.0" } }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -2615,6 +2726,12 @@ "dev": true, "optional": true }, + "remove-trailing-slash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==", + "dev": true + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", diff --git a/extensions/telemetry/package.json b/extensions/telemetry/package.json index 4fa6abbca9..3b6137b3ac 100644 --- a/extensions/telemetry/package.json +++ b/extensions/telemetry/package.json @@ -21,6 +21,7 @@ "mobx": "^5.15.5", "react": "^16.13.1", "node-machine-id": "^1.1.12", - "universal-analytics": "^0.4.23" + "universal-analytics": "^0.4.23", + "analytics-node": "^3.4.0-beta.3" } } diff --git a/extensions/telemetry/src/tracker.ts b/extensions/telemetry/src/tracker.ts index f6ac8c19a4..f2d3c6c918 100644 --- a/extensions/telemetry/src/tracker.ts +++ b/extensions/telemetry/src/tracker.ts @@ -1,31 +1,40 @@ import { EventBus, Util, Store, App } from "@k8slens/extensions" import ua from "universal-analytics" +import Analytics from "analytics-node" import { machineIdSync } from "node-machine-id" import { telemetryPreferencesStore } from "./telemetry-preferences-store" export class Tracker extends Util.Singleton { static readonly GA_ID = "UA-159377374-1" - + static readonly SEGMENT_KEY = "YENwswyhlOgz8P7EFKUtIZ2MfON7Yxqb" protected eventHandlers: Array<(ev: EventBus.AppEvent ) => void> = [] protected started = false protected visitor: ua.Visitor + protected analytics: Analytics protected machineId: string = null; protected ip: string = null; protected appVersion: string; protected locale: string; - protected electronUA: string; + protected userAgent: string; + protected anonymousId: string; + protected os: string protected reportInterval: NodeJS.Timeout private constructor() { super(); + this.anonymousId = machineIdSync() + this.os = this.resolveOS() + this.userAgent = `Lens ${App.version} (${this.os})` try { - this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false }) + this.visitor = ua(Tracker.GA_ID, this.anonymousId, { strictCidFormat: false }) } catch (error) { this.visitor = ua(Tracker.GA_ID) } + + this.analytics = new Analytics(Tracker.SEGMENT_KEY, { flushAt: 1 }) this.visitor.set("dl", "https://telemetry.k8slens.dev") - this.visitor.set("ua", `Lens ${App.version} (${this.getOS()})`) + this.visitor.set("ua", this.userAgent) } start() { @@ -38,6 +47,9 @@ export class Tracker extends Util.Singleton { } this.eventHandlers.push(handler) EventBus.appEventBus.addListener(handler) + } + + reportPeriodically() { this.reportInterval = setInterval(() => { this.reportData() }, 60 * 60 * 1000) // report every 1h @@ -61,12 +73,13 @@ export class Tracker extends Util.Singleton { } protected reportData() { - const clustersList = Store.clusterStore.clustersList + const clustersList = Store.clusterStore.enabledClustersList this.event("generic-data", "report", { appVersion: App.version, + os: this.os, clustersCount: clustersList.length, - workspacesCount: Store.workspaceStore.workspacesList.length + workspacesCount: Store.workspaceStore.enabledWorkspacesList.length }) clustersList.forEach((cluster) => { @@ -78,6 +91,7 @@ export class Tracker extends Util.Singleton { protected reportClusterData(cluster: Store.ClusterModel) { this.event("cluster-data", "report", { id: cluster.metadata.id, + managed: !!cluster.ownerRef, kubernetesVersion: cluster.metadata.version, distribution: cluster.metadata.distribution, nodesCount: cluster.metadata.nodes, @@ -85,7 +99,7 @@ export class Tracker extends Util.Singleton { }) } - protected getOS() { + protected resolveOS() { let os = "" if (App.isMac) { os = "MacOS" @@ -115,6 +129,19 @@ export class Tracker extends Util.Singleton { ea: eventAction, ...otherParams, }).send() + + this.analytics.track({ + anonymousId: this.anonymousId, + event: `${eventCategory} ${eventAction}`, + context: { + userAgent: this.userAgent, + }, + properties: { + category: eventCategory, + ...otherParams, + }, + + }) } catch (err) { console.error(`Failed to track "${eventCategory}:${eventAction}"`, err) } diff --git a/src/main/cluster-detectors/distribution-detector.ts b/src/main/cluster-detectors/distribution-detector.ts index 139150112f..b5895f8a71 100644 --- a/src/main/cluster-detectors/distribution-detector.ts +++ b/src/main/cluster-detectors/distribution-detector.ts @@ -31,7 +31,7 @@ export class DistributionDetector extends BaseClusterDetector { if (this.isCustom()) { return { value: "custom", accuracy: 10} } - return { value: "vanilla", accuracy: 10} + return { value: "unknown", accuracy: 10} } public async getKubernetesVersion() { diff --git a/src/main/cluster-detectors/nodes-count-detector.ts b/src/main/cluster-detectors/nodes-count-detector.ts index 42ddf28742..858ff43d9f 100644 --- a/src/main/cluster-detectors/nodes-count-detector.ts +++ b/src/main/cluster-detectors/nodes-count-detector.ts @@ -5,13 +5,12 @@ export class NodesCountDetector extends BaseClusterDetector { key = ClusterMetadataKey.NODES_COUNT public async detect() { + if (!this.cluster.accessible) return null; const nodeCount = await this.getNodeCount() return { value: nodeCount, accuracy: 100} } protected async getNodeCount(): Promise { - if (!this.cluster.accessible) return null; - const response = await this.k8sRequest("/api/v1/nodes") return response.items.length }