diff --git a/src/components/admin/ErrorDetailsModal.tsx b/src/components/admin/ErrorDetailsModal.tsx index 657d0897..d9bd92c4 100644 --- a/src/components/admin/ErrorDetailsModal.tsx +++ b/src/components/admin/ErrorDetailsModal.tsx @@ -57,8 +57,7 @@ Timestamp: ${format(new Date(error.created_at), 'PPpp')} Type: ${error.error_type} Endpoint: ${error.endpoint} Method: ${error.method} -Status: ${error.status_code} -Duration: ${error.duration_ms}ms +Status: ${error.status_code}${error.duration_ms != null ? `\nDuration: ${error.duration_ms}ms` : ''} Error Message: ${error.error_message} @@ -117,10 +116,12 @@ ${error.error_stack ? `Stack Trace:\n${error.error_stack}` : ''}

{error.status_code}

-
- -

{error.duration_ms}ms

-
+ {error.duration_ms != null && ( +
+ +

{error.duration_ms}ms

+
+ )} {error.user_id && (
diff --git a/src/lib/errorHandler.ts b/src/lib/errorHandler.ts index 4ef0909d..87a9f250 100644 --- a/src/lib/errorHandler.ts +++ b/src/lib/errorHandler.ts @@ -8,6 +8,7 @@ export type ErrorContext = { action: string; userId?: string; metadata?: Record; + duration?: number; // Optional: milliseconds the operation took }; export class AppError extends Error { @@ -78,6 +79,7 @@ export const handleError = ( p_breadcrumbs: JSON.stringify(breadcrumbs), p_timezone: envContext.timezone, p_referrer: document.referrer || undefined, + p_duration_ms: context.duration, }).then(({ error: dbError }) => { if (dbError) { logger.error('Failed to log error to database', { dbError }); @@ -161,6 +163,7 @@ export const handleNonCriticalError = ( p_breadcrumbs: JSON.stringify(breadcrumbs), p_timezone: envContext.timezone, p_referrer: document.referrer || undefined, + p_duration_ms: context.duration, }).then(({ error: dbError }) => { if (dbError) { logger.error('Failed to log non-critical error to database', { dbError }); @@ -202,3 +205,21 @@ export function hasErrorCode(error: unknown): error is { code: string } { typeof (error as { code: unknown }).code === 'string' ); } + +/** + * Helper to wrap async operations with automatic duration tracking + * Use this for operations where you want to track how long they took before failing + */ +export async function withErrorTiming( + fn: () => Promise, + errorContext: Omit +): Promise { + const start = performance.now(); + try { + return await fn(); + } catch (error) { + const duration = Math.round(performance.now() - start); + handleError(error, { ...errorContext, duration }); + throw error; + } +} diff --git a/src/pages/admin/ErrorMonitoring.tsx b/src/pages/admin/ErrorMonitoring.tsx index 0361af74..70894977 100644 --- a/src/pages/admin/ErrorMonitoring.tsx +++ b/src/pages/admin/ErrorMonitoring.tsx @@ -165,7 +165,7 @@ export default function ErrorMonitoring() {
ID: {error.request_id.slice(0, 8)} {format(new Date(error.created_at), 'PPp')} - {error.duration_ms}ms + {error.duration_ms != null && {error.duration_ms}ms}