@200systems/mf-logger
Version:
Structured logging with multiple outputs and performance timing for TypeScript applications
156 lines • 4.99 kB
JavaScript
import { ENV } from './environment.js';
// Node.js File Handler
export class NodeFileHandler {
config;
currentFileSize = 0;
fileIndex = 0;
initialized = false;
constructor(config) {
this.config = config;
}
async initialize() {
if (this.initialized)
return;
try {
const { mkdir, stat, writeFile } = await import('fs/promises');
const { dirname, resolve } = await import('path');
const dirPath = dirname(resolve(this.config.filePath));
await mkdir(dirPath, { recursive: true });
try {
const stats = await stat(this.getCurrentFilePath());
this.currentFileSize = stats.size;
}
catch {
// File doesn't exist, create it
await writeFile(this.getCurrentFilePath(), '', 'utf8');
this.currentFileSize = 0;
}
this.initialized = true;
}
catch (error) {
throw new Error(`Failed to initialize log file: ${error}`);
}
}
async write(content) {
if (!this.initialized) {
await this.initialize();
}
try {
const { appendFile } = await import('fs/promises');
const logSize = Buffer.byteLength(content, 'utf8');
if (this.config.maxSize && this.currentFileSize + logSize > this.config.maxSize) {
await this.rotate();
}
await appendFile(this.getCurrentFilePath(), content, 'utf8');
this.currentFileSize += logSize;
}
catch (error) {
console.error('Failed to write to log file:', error);
}
}
async rotate() {
this.fileIndex++;
this.currentFileSize = 0;
// Clean up old files
if (this.config.maxFiles && this.fileIndex >= this.config.maxFiles) {
const oldFileIndex = this.fileIndex - this.config.maxFiles;
const oldFilePath = this.getFilePathForIndex(oldFileIndex);
try {
const { unlink } = await import('fs/promises');
await unlink(oldFilePath);
}
catch {
// File might not exist, ignore error
}
}
}
getCurrentFilePath() {
return this.getFilePathForIndex(this.fileIndex);
}
getFilePathForIndex(index) {
if (index === 0) {
return this.config.filePath;
}
const extension = this.config.filePath.split('.').pop() || '';
const baseName = this.config.filePath.slice(0, -(extension.length + 1));
return `${baseName}.${index}.${extension}`;
}
}
// Browser Storage Handler
export class BrowserStorageHandler {
storageKey;
maxEntries;
storage;
constructor(config) {
// Use filename as storage key
const filename = config.filePath.split('/').pop() || 'app-logs';
this.storageKey = `mf-logger-${filename}`;
this.maxEntries = 1000; // Reasonable limit for browser storage
this.storage = globalThis.localStorage;
}
async initialize() {
// No initialization needed for browser storage
}
async write(content) {
try {
const logs = this.getLogs();
logs.push({
timestamp: new Date().toISOString(),
content: content.trim()
});
if (logs.length > this.maxEntries) {
logs.splice(0, logs.length - this.maxEntries);
}
this.storage.setItem(this.storageKey, JSON.stringify(logs));
}
catch (error) {
console.warn('Failed to write to browser storage:', error);
}
}
getLogs() {
try {
return JSON.parse(this.storage.getItem(this.storageKey) || '[]');
}
catch {
return [];
}
}
// Utility method to retrieve logs
retrieveLogs() {
return this.getLogs();
}
// Utility method to clear logs
clearLogs() {
this.storage.removeItem(this.storageKey);
}
}
// No-op Handler for unsupported environments
export class NoOpFileHandler {
config;
warned = false;
constructor(config) {
this.config = config;
}
async initialize() {
// No initialization needed
}
async write(content) {
if (!this.warned) {
console.warn(`File logging to '${this.config.filePath}' not available in this environment`);
this.warned = true;
}
}
}
// Factory function
export function createFileHandler(config) {
if (ENV.isNode) {
return new NodeFileHandler(config);
}
else if (ENV.isBrowser && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
return new BrowserStorageHandler(config);
}
else {
return new NoOpFileHandler(config);
}
}
//# sourceMappingURL=file-handlers.js.map