Skip to content

Commit

Permalink
event-tracking: Add event-tracking with PostHog
Browse files Browse the repository at this point in the history
For now, the following are tracked:
- App start
- Session time
- Video recording start/stop
- Video recording time
- Vehicle arming/disarming
  • Loading branch information
rafaellehmkuhl committed Dec 9, 2024
1 parent 2e12e06 commit 19f8986
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/libs/external-telemetry/event-tracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import posthog from 'posthog-js'

/**
* PostHog client
*/
class PostHog {
static posthog: ReturnType<typeof posthog.init> | undefined = undefined

/**
* Initialize the PostHog client if not already initialized
*/
constructor() {
if (!PostHog.posthog) {
PostHog.posthog = posthog.init('phc_SfqVeZcpYHmhUn9NRizThxFxiI9fKqvjRjmBDB8ToRs', {
api_host: 'https://us.i.posthog.com',
person_profiles: 'always', // Create profiles for anonymous users as well
})
}
}

/**
* Capture an event
* @param {string} eventName - The name of the event
* @param {Record<string, unknown>} properties - The properties of the event
*/
capture(eventName: string, properties?: Record<string, unknown>): void {
if (!PostHog.posthog) {
throw new Error('PostHog client not initialized.')
}
PostHog.posthog.capture(eventName, properties)
}
}

const eventTracker = new PostHog()
export default eventTracker
3 changes: 3 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { createApp } from 'vue'
import VueVirtualScroller from 'vue-virtual-scroller'

import { app_version } from '@/libs/cosmos'
import eventTracker from '@/libs/external-telemetry/event-tracking'

import App from './App.vue'
import vuetify from './plugins/vuetify'
Expand All @@ -25,6 +26,8 @@ loadFonts()

const app = createApp(App)

eventTracker.capture('App started')

// Initialize Sentry for error tracking
// Only track usage statistics if the user has not opted out and the app is not in development mode
if (window.localStorage.getItem('cockpit-enable-usage-statistics-telemetry') && import.meta.env.DEV === false) {
Expand Down
15 changes: 15 additions & 0 deletions src/stores/mainVehicle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useStorage, useTimestamp, watchThrottled } from '@vueuse/core'
import { differenceInSeconds } from 'date-fns'
import { defineStore } from 'pinia'
import { v4 as uuid } from 'uuid'
import { computed, reactive, ref, watch } from 'vue'
Expand All @@ -18,6 +19,7 @@ import { ConnectionManager } from '@/libs/connection/connection-manager'
import type { Package } from '@/libs/connection/m2r/messages/mavlink2rest'
import { MavAutopilot, MAVLinkType, MavType } from '@/libs/connection/m2r/messages/mavlink2rest-enum'
import type { Message } from '@/libs/connection/m2r/messages/mavlink2rest-message'
import eventTracker from '@/libs/external-telemetry/event-tracking'
import { availableCockpitActions, registerActionCallback } from '@/libs/joystick/protocols/cockpit-actions'
import { MavlinkManualControlManager } from '@/libs/joystick/protocols/mavlink-manual-control'
import type { ArduPilot } from '@/libs/vehicle/ardupilot/ardupilot'
Expand Down Expand Up @@ -121,6 +123,7 @@ export const useMainVehicleStore = defineStore('main-vehicle', () => {
const genericVariables: Record<string, unknown> = reactive({})
const availableGenericVariables = ref<string[]>([])
const usedGenericVariables = ref<string[]>([])
const vehicleArmingTime = ref<Date | undefined>(undefined)

const mode = ref<string | undefined>(undefined)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -387,7 +390,19 @@ export const useMainVehicleStore = defineStore('main-vehicle', () => {
Object.assign(attitude, newAttitude)
})
mainVehicle.value.onArm.add((armed: boolean) => {
const wasArmed = isArmed.value
isArmed.value = armed

// If the vehicle was already in the desired state or it's the first time we are checking, do not capture an event
if (wasArmed === undefined || wasArmed === armed) return

if (armed) {
vehicleArmingTime.value = new Date()
eventTracker.capture('Vehicle armed')
} else {
const armDurationInSeconds = differenceInSeconds(new Date(), vehicleArmingTime.value ?? new Date())
eventTracker.capture('Vehicle disarmed', { armDurationInSeconds })
}
})
mainVehicle.value.onTakeoff.add((inAir: boolean) => {
flying.value = inAir
Expand Down
8 changes: 8 additions & 0 deletions src/stores/omniscientLogger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WebRTCStats } from '@peermetrics/webrtc-stats'
import { useDocumentVisibility } from '@vueuse/core'
import { differenceInSeconds } from 'date-fns'
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'

Expand All @@ -8,6 +9,7 @@ import {
createCockpitActionVariable,
setCockpitActionVariableData,
} from '@/libs/actions/data-lake'
import eventTracker from '@/libs/external-telemetry/event-tracking'
import { WebRTCStatsEvent, WebRTCVideoStat } from '@/types/video'

import { useVideoStore } from './video'
Expand Down Expand Up @@ -275,6 +277,12 @@ export const useOmniscientLoggerStore = defineStore('omniscient-logger', () => {
}
})

// Routine to send a ping event to the event tracking system every 5 minutes
const initialTimestamp = new Date()
setInterval(() => {
eventTracker.capture('Ping', { runningTimeInSeconds: differenceInSeconds(new Date(), initialTimestamp) })
}, 1000 * 60 * 5)

return {
streamsFrameRateHistory,
appFrameRateHistory,
Expand Down
6 changes: 6 additions & 0 deletions src/stores/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useBlueOsStorage } from '@/composables/settingsSyncer'
import { useSnackbar } from '@/composables/snackbar'
import { WebRTCManager } from '@/composables/webRTC'
import { getIpsInformationFromVehicle } from '@/libs/blueos'
import eventTracker from '@/libs/external-telemetry/event-tracking'
import { availableCockpitActions, registerActionCallback } from '@/libs/joystick/protocols/cockpit-actions'
import { datalogger } from '@/libs/sensors-logging'
import { isEqual, sleep } from '@/libs/utils'
Expand Down Expand Up @@ -190,6 +191,10 @@ export const useVideoStore = defineStore('video', () => {
const stopRecording = (streamName: string): void => {
if (activeStreams.value[streamName] === undefined) activateStream(streamName)

const timeRecordingStart = activeStreams.value[streamName]?.timeRecordingStart
const durationInSeconds = timeRecordingStart ? differenceInSeconds(new Date(), timeRecordingStart) : undefined
eventTracker.capture('Video recording stop', { streamName, durationInSeconds })

activeStreams.value[streamName]!.timeRecordingStart = undefined

activeStreams.value[streamName]!.mediaRecorder!.stop()
Expand Down Expand Up @@ -249,6 +254,7 @@ export const useVideoStore = defineStore('video', () => {
* @param {string} streamName - Name of the stream
*/
const startRecording = async (streamName: string): Promise<void> => {
eventTracker.capture('Video recording start', { streamName: streamName })
if (activeStreams.value[streamName] === undefined) activateStream(streamName)

if (namesAvailableStreams.value.isEmpty()) {
Expand Down

0 comments on commit 19f8986

Please sign in to comment.