mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:51:14 -05:00
feat: Integrate Cronitor RUM
This commit is contained in:
16
package-lock.json
generated
16
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "vite_react_shadcn_ts",
|
"name": "vite_react_shadcn_ts",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@cronitorio/cronitor-rum": "^0.4.1",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -654,6 +655,15 @@
|
|||||||
"solid-js": "^1.8"
|
"solid-js": "^1.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@cronitorio/cronitor-rum": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cronitorio/cronitor-rum/-/cronitor-rum-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-myOOC8Cvv+881hasEV/fsnUswKXMVKfHtioZIy5SCcRPvH2jCV7T/DyZLXjm97FaeWIfTlu4s5P0dKW1zi7T3A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"web-vitals": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
@@ -12819,6 +12829,12 @@
|
|||||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/web-vitals": {
|
||||||
|
"version": "3.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz",
|
||||||
|
"integrity": "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@cronitorio/cronitor-rum": "^0.4.1",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
|
|||||||
18
src/App.tsx
18
src/App.tsx
@@ -5,6 +5,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
|
|||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||||
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
||||||
|
import * as Cronitor from '@cronitorio/cronitor-rum';
|
||||||
import { AuthProvider } from "@/hooks/useAuth";
|
import { AuthProvider } from "@/hooks/useAuth";
|
||||||
import { AuthModalProvider } from "@/contexts/AuthModalContext";
|
import { AuthModalProvider } from "@/contexts/AuthModalContext";
|
||||||
import { MFAStepUpProvider } from "@/contexts/MFAStepUpContext";
|
import { MFAStepUpProvider } from "@/contexts/MFAStepUpContext";
|
||||||
@@ -387,7 +388,19 @@ function AppContent(): React.JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = (): React.JSX.Element => (
|
const App = (): React.JSX.Element => {
|
||||||
|
// Initialize Cronitor RUM on app mount
|
||||||
|
useEffect(() => {
|
||||||
|
Cronitor.load("0b5d17d3f7625ce8766c2c4c85c1895d", {
|
||||||
|
debug: import.meta.env.DEV, // Enable debug logs in development only
|
||||||
|
trackMode: 'history', // Automatically track page views with React Router
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log successful initialization
|
||||||
|
console.log('[Cronitor] RUM initialized');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<AuthModalProvider>
|
<AuthModalProvider>
|
||||||
@@ -401,6 +414,7 @@ const App = (): React.JSX.Element => (
|
|||||||
{import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} position="bottom" />}
|
{import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} position="bottom" />}
|
||||||
<AnalyticsWrapper />
|
<AnalyticsWrapper />
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -58,15 +58,42 @@ export const breadcrumb = {
|
|||||||
level: 'info',
|
level: 'info',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track critical user actions in Cronitor
|
||||||
|
const criticalActions = ['submit', 'delete', 'update', 'create', 'login', 'logout'];
|
||||||
|
const isCritical = criticalActions.some(ca => action.toLowerCase().includes(ca));
|
||||||
|
|
||||||
|
if (isCritical && typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
window.cronitor.track('critical_user_action', {
|
||||||
|
action,
|
||||||
|
component,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
apiCall: (endpoint: string, method: string, status?: number) => {
|
apiCall: (endpoint: string, method: string, status?: number) => {
|
||||||
|
const isError = status && status >= 400;
|
||||||
|
|
||||||
breadcrumbManager.add({
|
breadcrumbManager.add({
|
||||||
category: 'api_call',
|
category: 'api_call',
|
||||||
message: `API ${method} ${endpoint}`,
|
message: `API ${method} ${endpoint}`,
|
||||||
level: status && status >= 400 ? 'error' : 'info',
|
level: isError ? 'error' : 'info',
|
||||||
data: { endpoint, method, status },
|
data: { endpoint, method, status },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track significant API calls in Cronitor
|
||||||
|
if (typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
// Only track errors and slow requests
|
||||||
|
if (isError || (status && status >= 500)) {
|
||||||
|
window.cronitor.track('api_error', {
|
||||||
|
endpoint,
|
||||||
|
method,
|
||||||
|
status,
|
||||||
|
severity: status >= 500 ? 'high' : 'medium',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stateChange: (description: string, data?: Record<string, any>) => {
|
stateChange: (description: string, data?: Record<string, any>) => {
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ import { supabase } from '@/integrations/supabase/client';
|
|||||||
import { breadcrumbManager } from './errorBreadcrumbs';
|
import { breadcrumbManager } from './errorBreadcrumbs';
|
||||||
import { captureEnvironmentContext } from './environmentContext';
|
import { captureEnvironmentContext } from './environmentContext';
|
||||||
|
|
||||||
|
// Cronitor RUM integration
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
cronitor?: {
|
||||||
|
track: (eventName: string, data?: Record<string, any>) => void;
|
||||||
|
error: (error: Error | string, metadata?: Record<string, any>) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type ErrorContext = {
|
export type ErrorContext = {
|
||||||
action: string;
|
action: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
@@ -123,6 +133,25 @@ export const handleError = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track error in Cronitor RUM
|
||||||
|
if (typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
try {
|
||||||
|
window.cronitor.error(
|
||||||
|
error instanceof Error ? error : new Error(errorMessage),
|
||||||
|
{
|
||||||
|
errorId,
|
||||||
|
errorName,
|
||||||
|
action: context.action,
|
||||||
|
userId: context.userId,
|
||||||
|
supabaseError: supabaseErrorDetails,
|
||||||
|
breadcrumbCount: breadcrumbManager.getAll().length,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (cronitorError) {
|
||||||
|
logger.error('Failed to track error in Cronitor', { cronitorError });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Log to database with breadcrumbs (non-blocking)
|
// Log to database with breadcrumbs (non-blocking)
|
||||||
try {
|
try {
|
||||||
const envContext = captureEnvironmentContext();
|
const envContext = captureEnvironmentContext();
|
||||||
@@ -218,6 +247,21 @@ export const handleNonCriticalError = (
|
|||||||
severity: 'low',
|
severity: 'low',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track non-critical error in Cronitor (lower severity)
|
||||||
|
if (typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
try {
|
||||||
|
window.cronitor.track('non_critical_error', {
|
||||||
|
errorId,
|
||||||
|
errorMessage,
|
||||||
|
action: context.action,
|
||||||
|
userId: context.userId,
|
||||||
|
severity: 'low',
|
||||||
|
});
|
||||||
|
} catch (cronitorError) {
|
||||||
|
logger.error('Failed to track non-critical error in Cronitor', { cronitorError });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Log to database with breadcrumbs (non-blocking, fire-and-forget)
|
// Log to database with breadcrumbs (non-blocking, fire-and-forget)
|
||||||
try {
|
try {
|
||||||
const envContext = captureEnvironmentContext();
|
const envContext = captureEnvironmentContext();
|
||||||
|
|||||||
@@ -243,9 +243,28 @@ export async function withRetry<T>(
|
|||||||
error: error instanceof Error ? error.message : String(error)
|
error: error instanceof Error ? error.message : String(error)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track exhausted retries in Cronitor
|
||||||
|
if (typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
window.cronitor.track('retries_exhausted', {
|
||||||
|
totalAttempts: config.maxAttempts,
|
||||||
|
error: error instanceof Error ? error.name : 'UnknownError',
|
||||||
|
severity: 'high',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track retry attempts in Cronitor
|
||||||
|
if (attempt > 0 && typeof window !== 'undefined' && window.cronitor) {
|
||||||
|
window.cronitor.track('retry_attempt', {
|
||||||
|
attempt: attempt + 1,
|
||||||
|
maxAttempts: config.maxAttempts,
|
||||||
|
error: error instanceof Error ? error.name : 'UnknownError',
|
||||||
|
willRetry: attempt < config.maxAttempts - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate delay for next attempt
|
// Calculate delay for next attempt
|
||||||
const delay = calculateBackoffDelay(attempt, config);
|
const delay = calculateBackoffDelay(attempt, config);
|
||||||
|
|
||||||
|
|||||||
23
src/types/cronitor.d.ts
vendored
Normal file
23
src/types/cronitor.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
declare module '@cronitorio/cronitor-rum' {
|
||||||
|
export interface CronitorConfig {
|
||||||
|
debug?: boolean;
|
||||||
|
trackMode?: 'history' | 'off';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function load(apiKey: string, config?: CronitorConfig): void;
|
||||||
|
|
||||||
|
export function track(eventName: string, data?: Record<string, any>): void;
|
||||||
|
|
||||||
|
export function error(error: Error | string, metadata?: Record<string, any>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
cronitor?: {
|
||||||
|
track: (eventName: string, data?: Record<string, any>) => void;
|
||||||
|
error: (error: Error | string, metadata?: Record<string, any>) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user