feat: error handling and logger

This commit is contained in:
2026-02-19 16:10:35 -08:00
parent 41ade975f9
commit f28e2e37c0
10 changed files with 248 additions and 3 deletions
@@ -0,0 +1,127 @@
/**
* @copyright 2026 NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment';
interface LogPayload {
level: 'debug' | 'info' | 'warn' | 'error';
message: string;
context?: string;
error?: {
name: string;
message: string;
stack?: string;
};
}
@Injectable({
providedIn: 'root'
})
export class ConsoleLoggerService {
private http = inject(HttpClient);
private originalConsole = {
log: console.log.bind(console),
error: console.error.bind(console),
warn: console.warn.bind(console),
debug: console.debug.bind(console),
info: console.info.bind(console)
};
/**
* Initialises the console override to pipe logs to the API.
*/
initialise(): void {
console.log = (...args: unknown[]) => {
this.originalConsole.log(...args);
this.sendLog('info', this.formatArgs(args));
};
console.info = (...args: unknown[]) => {
this.originalConsole.info(...args);
this.sendLog('info', this.formatArgs(args));
};
console.debug = (...args: unknown[]) => {
this.originalConsole.debug(...args);
this.sendLog('debug', this.formatArgs(args));
};
console.warn = (...args: unknown[]) => {
this.originalConsole.warn(...args);
this.sendLog('warn', this.formatArgs(args));
};
console.error = (...args: unknown[]) => {
this.originalConsole.error(...args);
// Check if the first argument is an Error object
if (args[0] instanceof Error) {
const error = args[0];
this.sendLog('error', error.message, 'Console', {
name: error.name,
message: error.message,
stack: error.stack
});
} else {
this.sendLog('error', this.formatArgs(args));
}
};
// Global error handlers
window.addEventListener('error', (event: ErrorEvent) => {
this.originalConsole.error('Uncaught Error:', event.error);
this.sendLog('error', event.message, 'Window Error', {
name: event.error?.name || 'Error',
message: event.message,
stack: event.error?.stack
});
});
window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
this.originalConsole.error('Unhandled Promise Rejection:', event.reason);
const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
this.sendLog('error', error.message, 'Unhandled Rejection', {
name: error.name,
message: error.message,
stack: error.stack
});
});
}
private formatArgs(args: unknown[]): string {
return args.map(arg => {
if (typeof arg === 'string') {
return arg;
}
if (arg instanceof Error) {
return `${arg.name}: ${arg.message}`;
}
try {
return JSON.stringify(arg);
} catch {
return String(arg);
}
}).join(' ');
}
private sendLog(level: LogPayload['level'], message: string, context?: string, error?: LogPayload['error']): void {
const payload: LogPayload = {
level,
message,
context,
error
};
this.http.post(`${environment.apiUrl}/log`, payload).subscribe({
error: (err) => {
this.originalConsole.error('Failed to send log to API:', err);
}
});
}
}