Capture branch metrics individually

- otherwise duplication of captured values grows as more metrics are added
- placed MetricsReporter in root +layout.svelte to accommodate future metrics
This commit is contained in:
Mattias Granlund 2024-07-04 01:43:10 +01:00
parent c8e7368df5
commit d150f61ed7
6 changed files with 38 additions and 46 deletions

View File

@ -1,36 +1,42 @@
<script lang="ts">
import { ProjectMetrics, type ProjectMetricsReport } from './projectMetrics';
import { Project } from '$lib/backend/projects';
import { persisted } from '$lib/persisted/persisted';
import { shallowEqual } from '$lib/utils/compare';
import { getContext } from '$lib/utils/context';
import posthog from 'posthog-js';
import { onMount } from 'svelte';
const projectMetrics = getContext(ProjectMetrics);
const project = getContext(Project);
const projectId = projectMetrics.getProjectId();
const lastReport = persisted<ProjectMetricsReport | undefined>(
undefined,
'projectMetrics-' + project.id
);
// Storing the last known values so we don't report same metrics twice
const lastReport = persisted<ProjectMetricsReport>({}, `projectMetrics-${projectId}`);
const hourMs = 60 * 60 * 1000;
let lastCapture: number | undefined;
let lastCapture: { [key: string]: number | undefined } = {};
let intervalId: any;
function sample() {
const report = projectMetrics.getReport();
if (!report) return;
const metrics = projectMetrics.getMetrics();
const projectId = projectMetrics.getProjectId();
const changed = !shallowEqual($lastReport, report);
const timeSinceCaptureMs = lastCapture ? lastCapture - Date.now() : undefined;
if (!metrics) return;
if (changed || (timeSinceCaptureMs && timeSinceCaptureMs > 24 * hourMs)) {
posthog.capture('metrics:branch_count', { ...report, changed });
lastReport.set(report);
lastCapture = Date.now();
// Capture only individual changes.
for (let [metric, value] of Object.entries(metrics)) {
const lastCaptureMs = lastCapture[metric];
if (
// If no previously recorded value.
!$lastReport[metric] ||
// Or the value has changed.
$lastReport[metric] !== value ||
// Or 24h have passed since metric was last caprured
(lastCaptureMs && lastCaptureMs - Date.now() > 24 * hourMs)
) {
posthog.capture(`metrics:${metric}`, { project_id: projectId, count: value });
lastCapture[metric] = Date.now();
}
}
lastReport.set(metrics);
}
onMount(() => {
@ -42,3 +48,5 @@
};
});
</script>
/// fdaslkdfja

View File

@ -1,23 +1,26 @@
export type ProjectMetricsReport = {
project_id?: string;
[key: string]: string | number | undefined;
};
export class ProjectMetrics {
metrics: { [key: string]: string | number } = {};
private projectId: string | undefined;
private metrics: { [key: string]: string | number } = {};
setProject(projectId: string) {
getProjectId() {
return this.projectId;
}
setProjectId(projectId: string) {
this.projectId = projectId;
this.metrics = {};
}
setMetric(key: string, value: string | number) {
this.metrics[key] = value;
}
getReport(): ProjectMetricsReport | undefined {
getMetrics(): ProjectMetricsReport | undefined {
if (!this.projectId) return;
if (Object.keys(this.metrics).length === 0) return;
return { ...this.metrics, project_id: this.projectId };
return this.metrics;
}
}

View File

@ -1,18 +0,0 @@
// https://dmitripavlutin.com/how-to-compare-objects-in-javascript/
export function shallowEqual(object1: any | undefined, object2: any | undefined) {
if (object1 === undefined || object2 === undefined) return;
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
if (object1[key] !== object2[key]) {
return false;
}
}
return true;
}

View File

@ -14,6 +14,8 @@
import PromptModal from '$lib/components/PromptModal.svelte';
import ShareIssueModal from '$lib/components/ShareIssueModal.svelte';
import { GitHubService } from '$lib/github/service';
import MetricsReporter from '$lib/metrics/MetricsReporter.svelte';
import { ProjectMetrics } from '$lib/metrics/projectMetrics';
import ToastController from '$lib/notifications/ToastController.svelte';
import { RemotesService } from '$lib/remotes/service';
import { setSecretsService } from '$lib/secrets/secretsService';
@ -51,6 +53,7 @@
setContext(RemotesService, data.remotesService);
setContext(AIPromptService, data.aiPromptService);
setContext(LineManagerFactory, data.lineManagerFactory);
setContext(ProjectMetrics, data.projectMetrics);
let shareIssueModal: ShareIssueModal;
@ -107,6 +110,7 @@
<AppUpdater />
<PromptModal />
<GlobalSettingsMenuAction />
<MetricsReporter />
<style lang="postcss">
.app-root {

View File

@ -11,8 +11,6 @@
import { ReorderDropzoneManagerFactory } from '$lib/dragging/reorderDropzoneManager';
import History from '$lib/history/History.svelte';
import { HistoryService } from '$lib/history/history';
import MetricsReporter from '$lib/metrics/MetricsReporter.svelte';
import { ProjectMetrics } from '$lib/metrics/projectMetrics';
import Navigation from '$lib/navigation/Navigation.svelte';
import { persisted } from '$lib/persisted/persisted';
import * as events from '$lib/utils/events';
@ -36,7 +34,6 @@
gbBranchActive$,
branchService,
branchController,
projectMetrics,
branchDragActionsFactory,
commitDragActionsFactory,
reorderDropzoneManagerFactory
@ -54,7 +51,6 @@
$: setContext(BaseBranchService, baseBranchService);
$: setContext(BaseBranch, baseBranch);
$: setContext(Project, project);
$: setContext(ProjectMetrics, projectMetrics);
$: setContext(BranchDragActionsFactory, branchDragActionsFactory);
$: setContext(CommitDragActionsFactory, commitDragActionsFactory);
$: setContext(ReorderDropzoneManagerFactory, reorderDropzoneManagerFactory);
@ -131,7 +127,6 @@
{/if}
<slot />
</div>
<MetricsReporter />
{/if}
{/key}

View File

@ -67,7 +67,7 @@ export async function load({ params, parent }) {
githubService,
branchController
);
projectMetrics.setProject(projectId);
projectMetrics.setProjectId(projectId);
const branchDragActionsFactory = new BranchDragActionsFactory(branchController);
const commitDragActionsFactory = new CommitDragActionsFactory(branchController, project);