Metalog
Metarhia logger 📝
Install / Use
/learn @metarhia/MetalogREADME
Meta Logger for Metarhia
Output example
<img src="https://user-images.githubusercontent.com/4405297/111154959-7b99c700-859c-11eb-81bb-0f8398535106.png" width="60%"/>Usage
const logger = await Logger.create({
path: './log', // absolute or relative path
workerId: 7, // mark for process or thread
flushInterval: 3000, // flush log to disk interval (default: 3s)
writeBuffer: 64 * 1024, // buffer size (default: 64kb)
keepDays: 5, // delete after N days, 0 - disable (default: 1)
home: process.cwd(), // remove substring from paths
json: false, // print logs in JSON format (default: false)
toFile: ['log', 'info', 'warn', 'error'], // tags to write to file (default: all)
toStdout: ['log', 'info', 'warn', 'error'], // tags to write to stdout (default: all)
createStream: () => fs.createWriteStream, // custom stream factory (optional)
crash: 'flush', // crash handling: 'flush' to flush buffer on exit (optional)
});
const { console } = logger;
console.log('Test message');
console.info('Info message');
console.warn('Warning message');
console.error('Error message');
console.debug('Debug message');
console.assert(true, 'Assertion passed');
console.assert(false, 'Assertion failed');
console.count('counter');
console.count('counter');
console.countReset('counter');
console.time('operation');
// ... some operation ...
console.timeEnd('operation');
console.timeLog('operation', 'Checkpoint');
console.group('Group 1');
console.log('Nested message');
console.groupCollapsed('Group 2');
console.log('Collapsed group message');
console.groupEnd();
console.groupEnd();
console.dir({ key: 'value' });
console.dirxml('<div>HTML content</div>');
console.table([
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
]);
console.trace('Trace message');
await logger.close();
Console API Compatibility
Metalog provides a fully compatible console implementation that supports all Node.js console methods:
console.log([data][, ...args])- General loggingconsole.info([data][, ...args])- Informational messagesconsole.warn([data][, ...args])- Warning messagesconsole.error([data][, ...args])- Error messagesconsole.debug([data][, ...args])- Debug messagesconsole.assert(value[, ...message])- Assertion testingconsole.clear()- Clear the consoleconsole.count([label])- Count occurrencesconsole.countReset([label])- Reset counterconsole.dir(obj[, options])- Object inspectionconsole.dirxml(...data)- XML/HTML inspectionconsole.group([...label])- Start groupconsole.groupCollapsed()- Start collapsed groupconsole.groupEnd()- End groupconsole.table(tabularData[, properties])- Table displayconsole.time([label])- Start timerconsole.timeEnd([label])- End timerconsole.timeLog([label][, ...data])- Log timer valueconsole.trace([message][, ...args])- Stack trace
All methods maintain the same behavior as Node.js native console, with output routed through the metalog system for consistent formatting and file logging.
Configuration Options
LoggerOptions
| Option | Type | Default | Description |
| --------------- | ---------- | ------------------------------------------- | --------------------------------------------------- |
| path | string | required | Directory path for log files (absolute or relative) |
| home | string | required | Base path to remove from stack traces |
| workerId | number | undefined | Worker/process identifier (appears as W0, W1, etc.) |
| flushInterval | number | 3000 | Flush buffer to disk interval in milliseconds |
| writeBuffer | number | 65536 | Buffer size threshold before flushing (64KB) |
| keepDays | number | 1 | Days to keep log files (0 = disable rotation) |
| json | boolean | false | Output logs in JSON format |
| toFile | string[] | ['log', 'info', 'warn', 'debug', 'error'] | Log tags to write to file |
| toStdout | string[] | ['log', 'info', 'warn', 'debug', 'error'] | Log tags to write to stdout |
| createStream | function | fs.createWriteStream | Custom stream factory function |
| crash | string | undefined | Crash handling mode ('flush' to flush on exit) |
Log Tags
Metalog supports five log tags that can be filtered independently for file and console output:
log- General logginginfo- Informational messageswarn- Warning messagesdebug- Debug messageserror- Error messages
Advanced Usage
Custom Stream Factory
const logger = await Logger.create({
path: './log',
home: process.cwd(),
createStream: (filePath) => {
// Custom compression stream
const fs = require('fs');
const zlib = require('zlib');
const gzip = zlib.createGzip();
const writeStream = fs.createWriteStream(filePath + '.gz');
return gzip.pipe(writeStream);
},
});
Selective Logging
// Only log errors to file, all tags to console
const logger = await Logger.create({
path: './log',
home: process.cwd(),
toFile: ['error'],
toStdout: ['log', 'info', 'warn', 'debug', 'error'],
});
// Only log info and above tags to both file and console
const logger = await Logger.create({
path: './log',
home: process.cwd(),
toFile: ['info', 'warn', 'error'],
toStdout: ['info', 'warn', 'error'],
});
JSON Logging
const logger = await Logger.create({
path: './log',
home: process.cwd(),
json: true,
});
logger.console.info('User action', { userId: 123, action: 'login' });
// Output: {"timestamp":"2025-01-07T10:30:00.000Z","worker":"W0","tag":"info","message":"User action","userId":123,"action":"login"}
Log Rotation and Cleanup
const logger = await Logger.create({
path: './log',
home: process.cwd(),
keepDays: 7, // Keep logs for 7 days
workerId: 1,
});
// Manual rotation
await logger.rotate();
// Log files are automatically rotated daily
// Old files are cleaned up based on keepDays setting
Error Handling
const logger = await Logger.create({
path: './log',
home: process.cwd(),
crash: 'flush', // Flush buffer on process exit
});
logger.on('error', (error) => {
console.error('Logger error:', error.message);
});
// Graceful shutdown
process.on('SIGTERM', async () => {
await logger.close();
process.exit(0);
});
API Reference
Logger Class
Constructor
new Logger(options: LoggerOptions): Promise<Logger>
Static Methods
Logger.create(options: LoggerOptions): Promise<Logger>- Create and open logger
Instance Methods
open(): Promise<Logger>- Open log file and start loggingclose(): Promise<void>- Close logger and flush remaining datarotate(): Promise<void>- Manually trigger log rotationwrite(tag: string, indent: number, args: unknown[]): void- Low-level write methodflush(callback?: (error?: Error) => void): void- Flush buffer to disk
Properties
active: boolean- Whether logger is currently activepath: string- Log directory pathhome: string- Home directory for path normalizationconsole: Console- Console instance for logging
BufferedStream Class
const stream = new BufferedStream({
stream: fs.createWriteStream('output.log'),
writeBuffer: 32 * 1024, // 32KB buffer
flushInterval: 5000, // 5 second flush interval
});
stream.write(Buffer.from('data'));
stream.flush();
await stream.close();
Formatter Class
const formatter = new Formatter({
worker: 'W1',
home: '/app',
json: false,
});
const formatted = formatter.formatPretty('info', 0, ['Message']);
const jsonOutput = formatter.formatJson('error', 0, [error]);
Best Practices
Performance Optimization
-
Buffer Size: Adjust
writeBufferbased on your log volume- High volume: 128KB or larger
- Low volume: 16KB or smaller
-
Flush Interval: Balance between performance and data safety
- Production: 3-10 seconds
- Development: 1-3 seconds
-
Selective Logging: Use
toFileandtoStdoutto reduce I/O// Production: Only errors to file, warnings+ to console toFile: ['error'], toStdout: ['warn', 'error']
Log Management
-
Rotation Strategy: Set appropriate
keepDaysbased on storage -
Path Organization: Use structured paths for multi-service deployments
path: `/var/log/app/${process.env.NODE_ENV}/${process.env.SERVICE_NAME}`; -
Error Handling: Always handle logger errors
logger.on('error', (error) => { // Fallback logging or alerting });
