mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-10-05 16:37:44 +03:00
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:
parent
c8e7368df5
commit
d150f61ed7
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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 {
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user