chore: complete update
This commit is contained in:
277
resources/js/core/logger-next.js
Normal file
277
resources/js/core/logger-next.js
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* Erweiterter Logger mit Processor-Architektur, Request-ID-Unterstützung und Server-Kommunikation.
|
||||
*/
|
||||
export class Logger {
|
||||
/**
|
||||
* Konfiguration des Loggers
|
||||
*/
|
||||
static config = {
|
||||
enabled: true,
|
||||
apiEndpoint: '/api/log',
|
||||
consoleEnabled: true,
|
||||
serverEnabled: true,
|
||||
minLevel: 'debug',
|
||||
};
|
||||
|
||||
/**
|
||||
* Liste der registrierten Processors
|
||||
*/
|
||||
static processors = [];
|
||||
|
||||
/**
|
||||
* Registrierte Handler
|
||||
*/
|
||||
static handlers = [];
|
||||
|
||||
/**
|
||||
* Aktive RequestID
|
||||
*/
|
||||
static requestId = null;
|
||||
|
||||
/**
|
||||
* Logger initialisieren
|
||||
*/
|
||||
static initialize(config = {}) {
|
||||
// Konfiguration überschreiben
|
||||
this.config = { ...this.config, ...config };
|
||||
|
||||
// Standard-Processors registrieren
|
||||
this.registerProcessor(this.requestIdProcessor);
|
||||
this.registerProcessor(this.timestampProcessor);
|
||||
|
||||
// Standard-Handler registrieren
|
||||
if (this.config.consoleEnabled) {
|
||||
this.registerHandler(this.consoleHandler);
|
||||
}
|
||||
|
||||
if (this.config.serverEnabled) {
|
||||
this.registerHandler(this.serverHandler);
|
||||
}
|
||||
|
||||
// Request-ID aus dem Document laden, wenn vorhanden
|
||||
if (typeof document !== 'undefined') {
|
||||
this.initFromDocument();
|
||||
}
|
||||
|
||||
// Unhandled Errors abfangen
|
||||
this.setupErrorHandling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug-Nachricht loggen
|
||||
*/
|
||||
static debug(...args) {
|
||||
this.log('debug', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Info-Nachricht loggen
|
||||
*/
|
||||
static info(...args) {
|
||||
this.log('info', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warnungs-Nachricht loggen
|
||||
*/
|
||||
static warn(...args) {
|
||||
this.log('warn', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fehler-Nachricht loggen
|
||||
*/
|
||||
static error(...args) {
|
||||
this.log('error', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log-Nachricht mit beliebigem Level erstellen
|
||||
*/
|
||||
static log(level, ...args) {
|
||||
if (!this.config.enabled) return;
|
||||
|
||||
// Level-Validierung
|
||||
const validLevels = ['debug', 'info', 'warn', 'error'];
|
||||
if (!validLevels.includes(level)) {
|
||||
level = 'info';
|
||||
}
|
||||
|
||||
// Nachricht und Kontext extrahieren
|
||||
const message = this.formatMessage(args);
|
||||
const context = args.find(arg => typeof arg === 'object' && arg !== null && !(arg instanceof Error)) || {};
|
||||
|
||||
// Exception extrahieren, falls vorhanden
|
||||
const error = args.find(arg => arg instanceof Error);
|
||||
if (error) {
|
||||
context.exception = error;
|
||||
}
|
||||
|
||||
// Log-Record erstellen
|
||||
let record = {
|
||||
level,
|
||||
message,
|
||||
context,
|
||||
timestamp: new Date(),
|
||||
extra: {},
|
||||
};
|
||||
|
||||
// Alle Processors durchlaufen
|
||||
this.processors.forEach(processor => {
|
||||
record = processor(record);
|
||||
});
|
||||
|
||||
// Log-Level-Prüfung (nach Processors, da sie das Level ändern könnten)
|
||||
const levelPriority = {
|
||||
debug: 100,
|
||||
info: 200,
|
||||
warn: 300,
|
||||
error: 400,
|
||||
};
|
||||
|
||||
if (levelPriority[record.level] < levelPriority[this.config.minLevel]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Alle Handler durchlaufen
|
||||
this.handlers.forEach(handler => {
|
||||
handler(record);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Nachricht aus verschiedenen Argumenten formatieren
|
||||
*/
|
||||
static formatMessage(args) {
|
||||
return args
|
||||
.filter(arg => !(arg instanceof Error) && (typeof arg !== 'object' || arg === null))
|
||||
.map(arg => String(arg))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Processor registrieren
|
||||
*/
|
||||
static registerProcessor(processor) {
|
||||
if (typeof processor !== 'function') return;
|
||||
this.processors.push(processor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler registrieren
|
||||
*/
|
||||
static registerHandler(handler) {
|
||||
if (typeof handler !== 'function') return;
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error-Handling-Setup
|
||||
*/
|
||||
static setupErrorHandling() {
|
||||
if (typeof window !== 'undefined') {
|
||||
// Unbehandelte Fehler abfangen
|
||||
window.addEventListener('error', (event) => {
|
||||
this.error('Unbehandelter Fehler:', event.error || event.message);
|
||||
});
|
||||
|
||||
// Unbehandelte Promise-Rejects abfangen
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
this.error('Unbehandelte Promise-Ablehnung:', event.reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request-ID aus dem Document laden
|
||||
*/
|
||||
static initFromDocument() {
|
||||
const meta = document.querySelector('meta[name="request-id"]');
|
||||
if (meta) {
|
||||
const fullRequestId = meta.getAttribute('content');
|
||||
// Nur den ID-Teil ohne Signatur verwenden
|
||||
this.requestId = fullRequestId.split('.')[0] || null;
|
||||
}
|
||||
}
|
||||
|
||||
/*** STANDARD-PROCESSORS ***/
|
||||
|
||||
/**
|
||||
* Processor für Request-ID
|
||||
*/
|
||||
static requestIdProcessor(record) {
|
||||
if (Logger.requestId) {
|
||||
record.extra.request_id = Logger.requestId;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processor für Timestamp-Formatierung
|
||||
*/
|
||||
static timestampProcessor(record) {
|
||||
record.formattedTimestamp = record.timestamp.toLocaleTimeString('de-DE');
|
||||
return record;
|
||||
}
|
||||
|
||||
/*** STANDARD-HANDLERS ***/
|
||||
|
||||
/**
|
||||
* Handler für Console-Ausgabe
|
||||
*/
|
||||
static consoleHandler(record) {
|
||||
const levelColors = {
|
||||
debug: 'color: gray',
|
||||
info: 'color: green',
|
||||
warn: 'color: orange',
|
||||
error: 'color: red',
|
||||
};
|
||||
|
||||
const color = levelColors[record.level] || 'color: black';
|
||||
const requestIdStr = record.extra.request_id ? `[${record.extra.request_id}] ` : '';
|
||||
const formattedMessage = `[${record.formattedTimestamp}] [${record.level.toUpperCase()}] ${requestIdStr}${record.message}`;
|
||||
|
||||
// Farbige Ausgabe in der Konsole
|
||||
console[record.level](
|
||||
`%c${formattedMessage}`,
|
||||
color,
|
||||
...(record.context ? [record.context] : [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler für Server-Kommunikation
|
||||
*/
|
||||
static serverHandler(record) {
|
||||
fetch(Logger.config.apiEndpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Request-ID': Logger.requestId || ''
|
||||
},
|
||||
body: JSON.stringify({
|
||||
level: record.level,
|
||||
message: record.message,
|
||||
context: record.context || {}
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
// Request-ID aus dem Header extrahieren
|
||||
const requestId = response.headers.get('X-Request-ID');
|
||||
if (requestId) {
|
||||
// Nur den ID-Teil ohne Signatur speichern
|
||||
const idPart = requestId.split('.')[0];
|
||||
if (idPart) {
|
||||
Logger.requestId = idPart;
|
||||
}
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch(() => {
|
||||
// Fehler beim Senden des Logs ignorieren (keine rekursive Fehlerbehandlung)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Standard-Initialisierung
|
||||
Logger.initialize();
|
||||
Reference in New Issue
Block a user