Lightweight Node.js logging with ECS-style fields, file rotation, optional HTTP output, and zero-dependency runtime API.
@timestamp,
log.level, message, service.name, etc.)dev mode)outputUri)config-sets)npm install log-report
const createLogger = require('log-report');
const log = createLogger('app');
log.info('Application started', { env: process.env.NODE_ENV });
log.debug('Cache primed', { entries: 42 });
log.warn('Disk space low', { percentFree: 7 });
log.error(new Error('Something went wrong'));
log.trace('db.query', { sql: 'SELECT 1' }); // visible in 'dev' or 'combined'
log.fatal('Cannot continue', { code: 'E_FATAL' }); // also goes to exception log
createLogger.clearLogFiles();
Possibilitys:
const createLogger = require('log-report');
const log = createLogger('payments', {
logMode: 'dev', // includes dev-style logs so trace is visible
outputJSON: false, // human-readable text mode
baseFields: {
'ECS.version': '1.12.0', // ECS version
service: { name: 'payment-service' }// ECS service.name
},
saveOnlyUncaughtException: false, // enable stdout/stderr hooks
logDir: './logs/log-report',
clearOnStartup: true, // start clean
addProcessTag: true, // adds process.pid
addFileTag: true, // adds log.origin.file.name
silent: false // also print to console
});
// Example logs
log.info('Payment service started');
log.trace('db.query', { sql: 'SELECT * FROM orders' });
log.error(new Error('Database connection failed'));
log.fatal('Cannot continue', { code: 'E_FATAL' });
log.trace('user.login', { userId: 12345, traceId: 'abcde12345' }); // trace helper
log.trace('cache.miss', 'Cache miss for user 12345', { key: 'user_12345' }); // with value
const createLogger = require('log-report');
const log = createLogger('app'); // uses default settings
log.info('App started');
const logReport = require('log-report');
// Modes: 'none' | 'short' | 'dev' | 'combined'
logReport.logMode = 'dev';
logReport.outputJSON = true; // JSON vs text
logReport.outputUri = 'http://localhost:5985/log-report'; // optional HTTP endpoint
logReport.logDir = './logs/log-report';
logReport.stdoutFileName = 'stdout.log';
logReport.stderrFileName = 'stderr.log';
logReport.exceptionFileName = 'exception.log';
logReport.clearOnStartup = true;
logReport.saveOnlyUncaughtException = false; // if true, stdout/stderr hooks are not attached
logReport.addProcessTag = true;
logReport.addFileTag = true;
logReport.silent = false; // print to console
logReport.loggingEnabled = true; // file logging on/off
logReport.compressEnabled = true; // gzip rotation over ~1MB
// Utility
logReport.clearLogFiles();
Install config-sets:
npm install config-sets
Then use in your app:
const configSets = require('config-sets');
const createLogger = require('log-report');
const log = createLogger('orders', configSets('log-report'));
// Example logs
log.info('Order service started');
log.trace('db.query', { sql: 'SELECT * FROM orders' });
log.error(new Error('Database connection failed'));
log.fatal('Cannot continue', { code: 'E_FATAL' });
log.trace('user.login', { userId: 12345, traceId: 'abcde12345' }); // trace helper
log.trace('cache.miss', 'Cache miss for user 12345', { key: 'user_12345' }); // with value
Tip: values also integrate with config-sets
(config-sets.json) under the log-report key.
Example config-sets.json:
{
/* Set to true to use production settings, */
/* false for development settings. Default is true. */
"isProduction": true,
/* Settings used in production mode. */
/* These settings are saved to "config-sets.json" file. */
"production": {
/* 'log-report' settings */
"log-report": {
/* Log mode. Default: 'short' */
/* Example values: 'none', 'short', 'dev', 'combined' */
/* 'none' = disable all logging except uncaughtException to exception.log */
/* 'short' = info, warn, error, fatal to respective files (stdout.log, stderr.log, exception.log) */
/* 'dev' = like 'short' but also includes debug and trace to stdout.log (for development) */
/* 'combined' = like 'dev' but also includes http info (if provided in log.trace) to stdout (logs all) */
/* 'dev' and 'combined' are verbose and may include sensitive information, use with caution in production. */
"logMode": "short",
/* Output format. Default: true (true = JSON format, false = text format) */
/* If false, the output format will be a human-readable text format. */
/* This is useful for development or debugging, but not recommended for production. */
"outputJSON": true,
"baseFields": {},
/* Output URI. Default: '' (e.g. 'http://localhost:3000/logs') */
/* If set, logs will be sent to the specified URI via HTTP POST in JSON format. */
/* Ensure that the URI is reachable and can accept POST requests with JSON payloads. */
/* If empty, logs will only be written to log files. */
"outputUri": "",
/* Specifies the directory where logs will be saved. Default: './logs/log-report' */
/* Ensure that the directory exists or the application has permission to create it. */
/* Relative paths are resolved against the current working directory. */
/* Absolute paths are used as-is. (e.g. '/var/log/myapp') */
"logDir": "./logs/log-report",
/* Specify appropriate log file names. Default: 'stdout.log' */
"stdoutFileName": "stdout.log",
/* Specify appropriate log file names. Default: 'stderr.log' */
"stderrFileName": "stderr.log",
/* Specify appropriate log file names. Default: 'exception.log' */
"exceptionFileName": "exception.log",
/* This is useful if you want to start with fresh log files, */
/* especially during development or testing. Default: true */
"clearOnStartup": true,
/* This is useful if you only want information about Uncaught Exception. Default: false */
"saveOnlyUncaughtException": true,
/* This is useful if you want to get information about the PID of a process, */
/* especially during development or testing. Default: false */
/* Note: In clustered environments, multiple processes may write to the same log file. */
/* Consider using 'addFileTag' for better traceability. */
"addProcessTag": false,
/* This is useful if you want to get information about the file that started the process, */
/* especially during development or testing. Default: false */
/* Note: In clustered environments, multiple processes may write to the same log file. */
/* This adds the 'log.origin.file.name' field to each log entry. */
"addFileTag": false,
/* Suppresses terminal output when set to true. Default: false */
/* When false, log messages will be printed to the console in addition to being saved to log files. */
/* When true, log messages will only be saved to log files. */
"silent": false,
/* Enable or disable logging. Default: true */
/* When false, no logs will be written to files, but uncaught exceptions will still be handled. */
/* This can be useful for temporarily disabling logging without changing other settings. */
"loggingEnabled": true,
/* Enable or disable gzip rotation over 1MB. Default: true */
/* When true, log files exceeding 1MB will be compressed using gzip to save disk space. */
/* When false, log files will grow indefinitely unless manually managed. */
"compressEnabled": true
}
},
/* Settings used in development mode. */
/* These settings are not saved to file and are created from production settings. */
/* You can override these settings using command line arguments. */
/* Set only the settings you want to change. */
"development": {}
}
Two formats are supported:
dev mode, text is
colorized and includes key=value pairs.Example JSON (abbreviated):
{ "@timestamp": "2025-01-01T12:00:00.000Z", "service.name": "app", "log.logger": "app", "log.level": "info", "message": "Application started", "event.kind": "event", "event.dataset": "app.stdout", "labels": { "env": "production" } }
Example text (abbreviated):
[2025-01-01T12:00:00.000Z] [info] Application started env=production
[2025-01-01T12:00:01.000Z] [debug] Cache primed entries=42
[2025-01-01T12:00:02.000Z] [warn] Disk space low percentFree=7
[2025-01-01T12:00:03.000Z] [error] Something went wrong Error: Something went wrong
at Object.<anonymous> (/path/to/file.js:10:15)
at Module._compile (internal/modules/cjs/loader.js:999:30)
...
[2025-01-01T12:00:04.000Z] [trace] db.query sql=SELECT 1
[2025-01-01T12:00:05.000Z] [fatal] Cannot continue code=E_FATAL
[2025-01-01T12:00:06.000Z] [trace] user.login trace.id=abcde12345 userId=12345
[2025-01-01T12:00:07.000Z] [trace] cache.miss Cache miss for user 12345 key=user_12345
The
trace() promotes common IDs to ECS fields:
trace.id from objKV.traceId or
objKV['trace.id']span.id from objKV.spanId or
objKV['span.id']transaction.id from
objKV.transactionId or objKV['transaction.id']Remaining key/values are placed under labels.*. If a
value argument is provided, it appears as labels.value.
Example:
log.trace('user.login', { userId: 12345, traceId: 'abcde12345' }); // traceId becomes trace.id
log.trace('cache.miss', { key: 'user_12345' }, 'Cache miss for user 12345'); // with value
If outputUri is set, logs are sent as JSON via HTTP
POST. File and console outputs still apply unless disabled.
When outputUri is set, each log is POSTed to the
endpoint as JSON. File and console outputs still apply unless disabled.
Example HTTP payload:
{ "@timestamp": "2025-01-01T12:00:00.000Z", "service.name": "app", "log.logger": "app", "log.level": "info", "message": "Application started", "event.kind": "event", "event.dataset": "app.stdout", "labels": { "env": "production" } }
Logs are buffered and flushed every second or when the buffer
exceeds 1KB. When a log file exceeds ~1MB, it is rotated and compressed with gzip (if
compressEnabled is true).
Uncaught exceptions are logged to exception.log and the
process exits. If saveOnlyUncaughtException is true, only uncaught exceptions are
recorded (stdout/stderr hooks are not attached).
Type definitions are included. If using TypeScript, import as below.
Ensure your tsconfig.json has "esModuleInterop": true or use
import * as createLogger from 'log-report' if not.
index.d.ts is included.
import createLogger from 'log-report';
const log = createLogger('api');
log.info('ready');
See examples/:
basic-ecs-json.js: JSON outputdev-trace-text.js: dev mode with trace and text
outputhttp-output.js: send logs to HTTP endpointrotate-compress.js: file rotation and gzipProject includes tests using test-runner-lite. No additional setup is needed. Use test-runner-lite (or Node directly) to run tests.
Run all tests:
npm test
Run the test directly:
node ./index.test.js
This project is licensed under the MIT License.
Copyright ยฉ Manuel Lรตhmus