@200systems/mf-logger
Version:
Structured logging with multiple outputs and performance timing for TypeScript applications
222 lines • 7.33 kB
JavaScript
import { createFileHandler, BrowserStorageHandler } from './file-handlers.js';
import { ENV } from './environment.js';
export class BaseOutput {
}
export class ConsoleOutput extends BaseOutput {
write(entry, formatted) {
const method = entry.level === 'error' ? 'error' : entry.level === 'warn' ? 'warn' : 'log';
console[method](formatted);
}
}
export class FileOutput extends BaseOutput {
filePath;
maxSize;
maxFiles;
fileHandler;
initPromise = null;
constructor(filePath, maxSize = 10 * 1024 * 1024, // 10MB
maxFiles = 5) {
super();
this.filePath = filePath;
this.maxSize = maxSize;
this.maxFiles = maxFiles;
this.fileHandler = createFileHandler({
filePath: this.filePath,
maxSize: this.maxSize,
maxFiles: this.maxFiles
});
}
async write(entry, formatted) {
// Ensure initialization happens only once
if (!this.initPromise) {
this.initPromise = this.fileHandler.initialize();
}
try {
await this.initPromise;
await this.fileHandler.write(formatted + '\n');
}
catch (error) {
console.error('Failed to write to file output:', error);
}
}
// Utility method for browser storage handler
retrieveLogs() {
if (this.fileHandler instanceof BrowserStorageHandler) {
return this.fileHandler.retrieveLogs();
}
return null;
}
clearLogs() {
if (this.fileHandler instanceof BrowserStorageHandler) {
this.fileHandler.clearLogs();
}
}
}
export class CustomOutput extends BaseOutput {
handler;
constructor(handler) {
super();
this.handler = handler;
}
async write(entry) {
try {
await this.handler(entry);
}
catch (error) {
console.error('Custom log handler failed:', error);
}
}
}
// Browser-specific output for structured logging
export class BrowserConsoleOutput extends BaseOutput {
write(entry, formatted) {
const styles = this.getConsoleStyles(entry.level);
if (ENV.isBrowser && console.groupCollapsed) {
console.groupCollapsed(`%c${entry.level.toUpperCase()}%c ${entry.message}`, styles.level, styles.message);
if (entry.metadata) {
console.log('Metadata:', entry.metadata);
}
if (entry.error) {
console.error('Error:', entry.error);
}
if (entry.performance) {
console.log('Performance:', entry.performance);
}
console.groupEnd();
}
else {
// Fallback to regular console
const method = entry.level === 'error' ? 'error' : entry.level === 'warn' ? 'warn' : 'log';
console[method](formatted);
}
}
getConsoleStyles(level) {
const baseStyle = 'padding: 2px 4px; border-radius: 2px; font-weight: bold;';
switch (level) {
case 'error':
return {
level: `${baseStyle} background: #ff4444; color: white;`,
message: 'color: #ff4444;'
};
case 'warn':
return {
level: `${baseStyle} background: #ffaa00; color: white;`,
message: 'color: #ffaa00;'
};
case 'info':
return {
level: `${baseStyle} background: #0088ff; color: white;`,
message: 'color: #0088ff;'
};
case 'debug':
return {
level: `${baseStyle} background: #888888; color: white;`,
message: 'color: #888888;'
};
default:
return {
level: baseStyle,
message: ''
};
}
}
}
// Remote logging output for browser environments
export class RemoteOutput extends BaseOutput {
endpoint;
batchSize;
flushInterval;
constructor(endpoint, batchSize = 10, flushInterval = 5000 // 5 seconds
) {
super();
this.endpoint = endpoint;
this.batchSize = batchSize;
this.flushInterval = flushInterval;
this.startBatchProcessor();
}
batch = [];
batchTimer = null;
async write(entry) {
this.batch.push(entry);
if (this.batch.length >= this.batchSize) {
await this.flush();
}
}
startBatchProcessor() {
this.batchTimer = setInterval(() => {
if (this.batch.length > 0) {
this.flush().catch(error => {
console.error('Failed to flush log batch:', error);
});
}
}, this.flushInterval);
}
async flush() {
if (this.batch.length === 0)
return;
const logsToSend = this.batch.splice(0);
try {
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ logs: logsToSend }),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
}
catch (error) {
console.error('Failed to send logs to remote endpoint:', error);
// Re-add logs to batch for retry (optional)
this.batch.unshift(...logsToSend);
}
}
// Cleanup method
destroy() {
if (this.batchTimer) {
clearInterval(this.batchTimer);
this.batchTimer = null;
}
if (this.batch.length > 0) {
this.flush().catch(console.error);
}
}
}
export function createOutput(config) {
switch (config.type) {
case 'console': {
const options = config.options;
// Use enhanced browser console in browser environments
if (ENV.isBrowser && options?.enhanced !== false) {
return new BrowserConsoleOutput();
}
return new ConsoleOutput();
}
case 'file': {
const options = config.options;
if (!options?.filePath) {
throw new Error('File output requires filePath option');
}
return new FileOutput(options.filePath, options.maxSize, options.maxFiles);
}
case 'custom': {
const options = config.options;
if (!options?.customHandler) {
throw new Error('Custom output requires customHandler option');
}
return new CustomOutput(options.customHandler);
}
case 'remote': {
const options = config.options;
if (!options?.endpoint) {
throw new Error('Remote output requires endpoint option');
}
return new RemoteOutput(options.endpoint, options.batchSize, options.flushInterval);
}
default:
throw new Error(`Unknown output type: ${config.type}`);
}
}
//# sourceMappingURL=outputs.js.map