fix: improve error handling and stability - no logout on transient errors, add ErrorBoundary, global error logging
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Global Error Logger
|
||||
*
|
||||
* Centralized error collection and reporting.
|
||||
* - Always logs to console.error
|
||||
* - Optionally POSTs to /api/v1/errors (silently fails if endpoint unavailable)
|
||||
* - Buffers errors in sessionStorage (ring buffer, max 50)
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = 'leocrm_error_buffer';
|
||||
const MAX_BUFFER_SIZE = 50;
|
||||
|
||||
interface ErrorEntry {
|
||||
timestamp: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
context?: Record<string, any>;
|
||||
url?: string;
|
||||
userAgent?: string;
|
||||
}
|
||||
|
||||
function getBuffer(): ErrorEntry[] {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return [];
|
||||
return JSON.parse(raw) as ErrorEntry[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveBuffer(buffer: ErrorEntry[]): void {
|
||||
try {
|
||||
// Ring buffer: keep only the last MAX_BUFFER_SIZE entries
|
||||
const trimmed = buffer.slice(-MAX_BUFFER_SIZE);
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(trimmed));
|
||||
} catch {
|
||||
// sessionStorage might be full or unavailable — silently ignore
|
||||
}
|
||||
}
|
||||
|
||||
function addToBuffer(entry: ErrorEntry): void {
|
||||
const buffer = getBuffer();
|
||||
buffer.push(entry);
|
||||
saveBuffer(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error with optional context.
|
||||
* Always writes to console.error.
|
||||
* Attempts to POST to /api/v1/errors (silently fails if unavailable).
|
||||
* Buffers in sessionStorage as a ring buffer (max 50 entries).
|
||||
*/
|
||||
export function logError(error: Error | string, context?: Record<string, any>): void {
|
||||
const message = typeof error === 'string' ? error : error.message;
|
||||
const stack = typeof error === 'string' ? undefined : error.stack;
|
||||
|
||||
const entry: ErrorEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
message,
|
||||
stack,
|
||||
context,
|
||||
url: typeof window !== 'undefined' ? window.location.href : undefined,
|
||||
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
|
||||
};
|
||||
|
||||
// Always log to console
|
||||
console.error('[ErrorLogger]', message, { stack, context, url: entry.url });
|
||||
|
||||
// Buffer in sessionStorage
|
||||
addToBuffer(entry);
|
||||
|
||||
// Attempt to send to backend (fire-and-forget, silently fail)
|
||||
try {
|
||||
fetch('/api/v1/errors', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(entry),
|
||||
}).catch(() => {
|
||||
// Silently fail — endpoint may not exist or network may be down
|
||||
});
|
||||
} catch {
|
||||
// Silently fail
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the buffered errors from sessionStorage.
|
||||
*/
|
||||
export function getErrorBuffer(): ErrorEntry[] {
|
||||
return getBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the error buffer from sessionStorage.
|
||||
*/
|
||||
export function clearErrorBuffer(): void {
|
||||
try {
|
||||
sessionStorage.removeItem(STORAGE_KEY);
|
||||
} catch {
|
||||
// Silently ignore
|
||||
}
|
||||
}
|
||||
|
||||
export default { logError, getErrorBuffer, clearErrorBuffer };
|
||||
Reference in New Issue
Block a user