Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
179 lines
5.1 KiB
TypeScript
179 lines
5.1 KiB
TypeScript
import { PerformanceTestResult } from './perf-utils'
|
|
|
|
export interface AnalyticsConfig {
|
|
enabled: boolean
|
|
projectKey?: string
|
|
apiHost?: string
|
|
}
|
|
|
|
export class PerformanceAnalyticsReporter {
|
|
private config: AnalyticsConfig
|
|
|
|
constructor(config: AnalyticsConfig = { enabled: false }) {
|
|
this.config = config
|
|
}
|
|
|
|
async reportPerformanceMetrics(
|
|
results: PerformanceTestResult[],
|
|
testContext: {
|
|
testName: string
|
|
timestamp: string
|
|
gitCommit?: string
|
|
branch?: string
|
|
ciEnvironment?: string
|
|
}
|
|
): Promise<void> {
|
|
if (!this.config.enabled) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(
|
|
'Analytics reporting disabled - would have sent:',
|
|
results.length,
|
|
'performance results'
|
|
)
|
|
return
|
|
}
|
|
|
|
try {
|
|
const payload = this.formatPayload(results, testContext)
|
|
// See https://eu.posthog.com/project/45972/insights/ZdpGyj1r
|
|
await this.sendToPostHog(payload)
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Sent ${results.length} performance metrics to PostHog`)
|
|
} catch (error) {
|
|
console.error('Failed to send performance metrics:', error)
|
|
}
|
|
}
|
|
|
|
private formatPayload(results: PerformanceTestResult[], testContext: any) {
|
|
return results.map((result) => ({
|
|
event: 'performance_test_result',
|
|
properties: {
|
|
// Test identification
|
|
test_name: testContext.testName,
|
|
interaction: result.interaction,
|
|
timestamp: testContext.timestamp,
|
|
|
|
// Environment
|
|
platform: result.environment.platform,
|
|
viewport: result.environment.viewport,
|
|
browser: result.environment.browser,
|
|
|
|
// Performance metrics
|
|
avg_fps: result.metrics.averageFps,
|
|
min_fps: result.metrics.minFps,
|
|
max_fps: result.metrics.maxFps,
|
|
total_frames: result.metrics.totalFrames,
|
|
duration_seconds: result.metrics.duration,
|
|
sample_count: result.metrics.samples.length,
|
|
|
|
// Comparison results
|
|
baseline_avg_fps: result.comparison.baseline.avgFps,
|
|
fps_change_percent: result.comparison.avgFpsChange,
|
|
is_regression: result.comparison.isRegression,
|
|
is_improvement: result.comparison.isImprovement,
|
|
status: result.comparison.status,
|
|
|
|
// CI/Git context
|
|
git_commit: testContext.gitCommit,
|
|
git_branch: testContext.branch,
|
|
ci_environment: testContext.ciEnvironment,
|
|
|
|
// Additional metadata
|
|
fps_samples: result.metrics.samples.slice(0, 10), // First 10 samples for analysis
|
|
},
|
|
}))
|
|
}
|
|
|
|
private async sendToPostHog(events: any[]): Promise<void> {
|
|
if (!this.config.projectKey) {
|
|
throw new Error('PostHog project key not configured')
|
|
}
|
|
|
|
const url = `${this.config.apiHost || 'https://analytics.tldraw.com'}/capture/`
|
|
|
|
for (const event of events) {
|
|
const payload = {
|
|
api_key: this.config.projectKey,
|
|
event: event.event,
|
|
properties: {
|
|
...event.properties,
|
|
distinct_id: `e2e-test-${Date.now()}`, // Anonymous test identifier
|
|
$lib: 'tldraw-e2e-perf',
|
|
$lib_version: '1.0.0',
|
|
},
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`PostHog API error: ${response.status} ${response.statusText}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
async reportRegressionAlert(result: PerformanceTestResult, testContext: any): Promise<void> {
|
|
if (!this.config.enabled || result.comparison.status !== 'fail') {
|
|
return
|
|
}
|
|
|
|
try {
|
|
const alertPayload = {
|
|
event: 'performance_regression_alert',
|
|
properties: {
|
|
test_name: testContext.testName,
|
|
interaction: result.interaction,
|
|
avg_fps_drop: Math.abs(result.comparison.avgFpsChange),
|
|
current_fps: result.metrics.averageFps,
|
|
baseline_fps: result.comparison.baseline.avgFps,
|
|
platform: result.environment.platform,
|
|
browser: result.environment.browser,
|
|
timestamp: testContext.timestamp,
|
|
git_commit: testContext.gitCommit,
|
|
git_branch: testContext.branch,
|
|
alert_message: result.comparison.message,
|
|
severity: result.comparison.avgFpsChange < -20 ? 'high' : 'medium',
|
|
},
|
|
}
|
|
|
|
await this.sendToPostHog([alertPayload])
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Sent performance regression alert for ${result.interaction}`)
|
|
} catch (error) {
|
|
console.error('Failed to send regression alert:', error)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Factory function to create analytics reporter with environment-based config
|
|
export function createAnalyticsReporter(): PerformanceAnalyticsReporter {
|
|
const config: AnalyticsConfig = {
|
|
enabled: process.env.PERFORMANCE_ANALYTICS_ENABLED === 'true',
|
|
projectKey: 'phc_i8oKgMzgV38sn3GfjswW9mevQ3gFlo7bJXekZFeDN6',
|
|
apiHost: process.env.POSTHOG_API_HOST || 'https://analytics.tldraw.com/ingest',
|
|
}
|
|
|
|
return new PerformanceAnalyticsReporter(config)
|
|
}
|
|
|
|
// Helper to collect test context from environment
|
|
export function getTestContext(testName: string) {
|
|
return {
|
|
testName,
|
|
timestamp: new Date().toISOString(),
|
|
gitCommit: process.env.GIT_COMMIT || process.env.GITHUB_SHA,
|
|
branch: process.env.GIT_BRANCH || process.env.GITHUB_REF_NAME,
|
|
ciEnvironment: process.env.CI
|
|
? process.env.GITHUB_ACTIONS
|
|
? 'github-actions'
|
|
: 'ci'
|
|
: 'local',
|
|
}
|
|
}
|