Skip to content

Commit

Permalink
chore: switch telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
terales committed Aug 27, 2023
1 parent c0aab21 commit 56672e4
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 148 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"test:update": "cross-env CHAI_JEST_SNAPSHOT_UPDATE_ALL=true nr test:unit"
},
"dependencies": {
"@amplitude/analytics-node": "^1.3.3",
"@babel/parser": "^7.15.3",
"@babel/traverse": "^7.15.0",
"bcp-47-normalize": "^2.0.1",
Expand Down Expand Up @@ -1309,5 +1310,6 @@
"hidden": true
}
]
}
},
"packageManager": "[email protected]"
}
1 change: 0 additions & 1 deletion src/core/Global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ export class Global {
commands.executeCommand('setContext', 'i18n-ally.extract.autoDetect', Config.extractAutoDetect)

Telemetry.track(TelemetryKey.Enabled)
Telemetry.updateUserProperties()

await this.initLoader(this._rootpath, reload)
}
Expand Down
207 changes: 61 additions & 146 deletions src/core/Telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from 'axios'
import { v4 as uuid } from 'uuid'
import { randomUUID } from 'crypto'
import * as amplitude from '@amplitude/analytics-node'
import { version } from '../../package.json'
import { Config } from './Config'
import { Log } from '~/utils'
Expand All @@ -8,10 +8,17 @@ import { Global } from '~/extension'
import { LocaleTreeItem, ProgressSubmenuItem } from '~/views'
import { CommandOptions } from '~/commands/manipulations/common'

const HEAP_ID_DEV = '1082064308'
const HEAP_ID_PROD = '4118173713'
const AMPLIDUTE_API = isProd
? '6459b23f8052370ad10d7734c7822d36' // Live
: '6459b23f8052370ad10d7734c7822d36' // Dev

const HEAP_ID = isProd ? HEAP_ID_PROD : HEAP_ID_DEV
const AMPLIDUTE_SERVER_ZONE = 'EU'

const AMPLIDUTE_FLUSH_QUEUE_SIZE = 100

const AMPLIDUTE_FLUSH_INTERVAL_MILLIS = isProd
? 5 * 60 * 1000 // 5 minutes
: 10 * 1000 // 10 seconds

export enum TelemetryKey {
Activated = 'activated',
Expand Down Expand Up @@ -46,96 +53,24 @@ export enum ActionSource {
Review = 'review'
}

export interface TelemetryEvent {
event: TelemetryKey
timestamp: number
identity: string
properties?: Record<string, any>
}

export class Telemetry {
private static _id: string
private static _timer: any = null

static events: TelemetryEvent[] = []

static get userId() {
if (this._id)
return this._id
this._id = Config.ctx.globalState.get('i18n-ally.telemetry-user-id')!
if (!this._id) {
this._id = uuid()
Config.ctx.globalState.update('i18n-ally.telemetry-user-id', this._id)
}
Log.info(`📈 Telemetry id: ${this._id}`)

return this._id
}

static checkVersionChange() {
const previousVersion = Config.ctx.globalState.get('i18n-ally.previous-version')

if (!previousVersion)
Telemetry.track(TelemetryKey.Installed, { new_version: version })
else if (previousVersion !== version)
Telemetry.track(TelemetryKey.Updated, { new_version: version, previous_version: previousVersion })

Config.ctx.globalState.update('i18n-ally.previous-version', version)
}

static get isEnabled() {
return Config.telemetry && !isTest
}
private static _userProperties: object
private static _amplitude: amplitude.Types.NodeClient

static async track(key: TelemetryKey, properties?: Record<string, any>, immediate = false) {
if (!this.isEnabled)
return
try {
this._initializeAmplitude()

const event: TelemetryEvent = {
event: key,
identity: this.userId,
timestamp: +new Date(),
properties,
}
this._amplitude.track(key, properties, this._getUserProperties())

if (isDev)
Log.info(`[telemetry] ${key}: ${JSON.stringify(properties)}`)

if (immediate) {
try {
await axios({
url: 'https://heapanalytics.com/api/track',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: {
app_id: HEAP_ID,
...event,
},
})
}
catch (e) {
Log.error(e, false)
}
}
else {
this.events.push(event)
this.schedule()
}
}
if (immediate)
this._amplitude.flush()

static schedule() {
if (this.events.length >= 100) {
this.sendBulk()
if (isDev)
Log.info(`[telemetry] ${key}: ${JSON.stringify(properties)}`)
}
else if (!this._timer && this.events.length) {
this._timer = setInterval(
() => this.sendBulk(),
isDev
? 10 * 1000 // 10 seconds
: 5 * 60 * 1000, // 5 minutes
)
catch (e) {
Log.error(e, false)
}
}

Expand All @@ -145,73 +80,53 @@ export class Telemetry {
: item?.actionSource || ActionSource.CommandPattele
}

static async updateUserProperties() {
if (!this.isEnabled)
private static _initializeAmplitude() {
if (this._amplitude)
return

const data = {
version,
feature_auto_detection: !!Config.autoDetection,
feature_annotation_in_place: !!Config.annotationInPlace,
feature_annotations: !!Config.annotations,
feature_disable_path_parsing: !!Config.disablePathParsing,
feature_extract_auto_detect: !!Config.extractAutoDetect,
feature_keep_fulfilled: !!Config.keepFulfilled,
feature_prefer_editor: !!Config.preferEditor,
feature_review_enabled: !!Config.reviewEnabled,
feature_has_path_matcher: !!Config._pathMatcher,
feature_has_custom_framework: !!Global.enabledFrameworks.find(i => i.id === 'custom'),
}
this._amplitude = amplitude.createInstance()

if (isDev)
Log.info(`[telemetry] user: ${JSON.stringify(data)}`)
this._amplitude.init(AMPLIDUTE_API, {
optOut: !Config.telemetry || isTest,
serverZone: AMPLIDUTE_SERVER_ZONE,
flushQueueSize: AMPLIDUTE_FLUSH_QUEUE_SIZE,
flushIntervalMillis: AMPLIDUTE_FLUSH_INTERVAL_MILLIS,
})

try {
await axios({
url: 'https://heapanalytics.com/api/add_user_properties',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: {
app_id: HEAP_ID,
identity: this.userId,
properties: data,
},
})
}
catch (e) {
Log.error(e, false)
}
this._amplitude.identify(new amplitude.Identify(), this._getUserProperties())
}

static async sendBulk() {
if (!this.events.length) {
clearInterval(this._timer)
this._timer = null
return
private static _getUserId() {
let userId = Config.ctx.globalState.get('i18n-ally.telemetry-user-id') ?? ''
if (!userId) {
userId = randomUUID()
Config.ctx.globalState.update('i18n-ally.telemetry-user-id', userId)
}
Log.info(`📈 Telemetry id: ${userId}`)
return userId
}

if (isDev)
Log.info('[telemetry] sending bulk')

const events = Array.from(this.events)
this.events.length = 0
try {
await axios({
url: 'https://heapanalytics.com/api/track',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: {
app_id: HEAP_ID,
events,
private static _getUserProperties() {
if (!this._userProperties) {
this._userProperties = {
user_id: this._getUserId(),
app_version: version,
user_properties: {
feature_auto_detection: !!Config.autoDetection,
feature_annotation_in_place: !!Config.annotationInPlace,
feature_annotations: !!Config.annotations,
feature_disable_path_parsing: !!Config.disablePathParsing,
feature_extract_auto_detect: !!Config.extractAutoDetect,
feature_keep_fulfilled: !!Config.keepFulfilled,
feature_prefer_editor: !!Config.preferEditor,
feature_review_enabled: !!Config.reviewEnabled,
feature_has_path_matcher: !!Config._pathMatcher,
feature_has_custom_framework: !!Global.enabledFrameworks.find(i => i.id === 'custom'),
feature_frameworks: Global.enabledFrameworks.map(i => i.display),
},
})
}
catch (e) {
Log.error(e, false)
}
}

return this._userProperties
}
}
27 changes: 27 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@
# yarn lockfile v1


"@amplitude/analytics-core@^1.2.3":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@amplitude/analytics-core/-/analytics-core-1.2.3.tgz#ab7adbb07a18190d5cfad992962a430679575c98"
integrity sha512-3WADE8IcxU7ZERMkBb0+JP7t6EekyFPM0djtNKXQaxgGgH3oqQzMmBCg19UnYYiBSHrZkpiMBLHNAvXL6HM7zg==
dependencies:
"@amplitude/analytics-types" "^1.3.3"
tslib "^2.4.1"

"@amplitude/analytics-node@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@amplitude/analytics-node/-/analytics-node-1.3.3.tgz#cc91f3512caedd8d5f1d14f1a2bbe1541adb743c"
integrity sha512-G+5EoGcVOOclcNRR57AQcGqg46xNXjG6tsSdF71+Npzc6zTfvDzKDY97ZULBju9TT/FXvhP8LnppAI8umT6qkQ==
dependencies:
"@amplitude/analytics-core" "^1.2.3"
"@amplitude/analytics-types" "^1.3.3"
tslib "^2.4.1"

"@amplitude/analytics-types@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@amplitude/analytics-types/-/analytics-types-1.3.3.tgz#c7b2a21e6ab0eb1670cce4d03127b62c373c6ed4"
integrity sha512-V4/h+izhG7NyVfIva1uhe6bToI/l5n+UnEomL3KEO9DkFoKiOG7KmXo/fmzfU6UmD1bUEWmy//hUFF16BfrEww==

"@antfu/eslint-config-basic@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@antfu/eslint-config-basic/-/eslint-config-basic-0.7.0.tgz#ad786cd27076d3f6b7c0df16af6cdc254139309c"
Expand Down Expand Up @@ -10677,6 +10699,11 @@ tslib@^2.0.3, tslib@^2.2.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==

tslib@^2.4.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==

tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
Expand Down

0 comments on commit 56672e4

Please sign in to comment.