perfect-logger
v3.0.0
Published
A zero-dependency, isomorphic logger for Node.js and Browsers with plugin support.
Maintainers
Readme
perfect-logger
A modern, powerful, and lightweight logging library for TypeScript and JavaScript applications. It is designed to be highly configurable and extensible, allowing you to tailor your logging output to your specific needs.
Features
- Multiple Appenders: Log to the console, file system, or extend it with your own custom appenders.
- Highly Configurable: Customize log formats, levels, and destinations.
- Log Rotation: Automatically rotate log files based on size, time (daily/hourly), or a combination.
- TypeScript Native: Written in TypeScript, providing strong typing and excellent editor support.
- Environment Aware: Works seamlessly in Node.js and modern browsers (features like the
FileAppenderare Node.js-only). - Zero Dependencies: A lightweight library with no external dependencies.
Installation
npm install perfect-loggerQuick Start
Get up and running with a few lines of code.
import { LogManager, ConsoleAppender, LogLevel } from 'perfect-logger';
// 1. Configure the LogManager
LogManager.configure({
appenders: [
new ConsoleAppender({ minLevel: LogLevel.INFO })
]
});
// 2. Get a logger instance
const logger = LogManager.getLogger('my-app');
// 3. Start logging!
logger.info('Application starting...');
logger.warn('Configuration is missing a recommended setting.');
logger.error('Failed to connect to the database.', new Error('Connection timed out'));
logger.debug('This is a debug message.', { userId: 123 }); // This won't be logged due to minLevelExample Outputs
Console Output
Using the default ConsoleAppender configuration, your output will look clean and simple. Note that console.error will automatically print the stack trace of an Error object.
2023/10/28 | 14:30:05.123 | INFO | my-app | Application starting...
2023/10/28 | 14:30:05.125 | WARN | my-app | Configuration is missing a recommended setting.
2023/10/28 | 14:30:05.128 | ERROR | my-app | Failed to connect to the database.
Error: Connection timed out
at <stack trace ...>File Output
The FileAppender provides more detailed output, especially for context objects and errors, which are formatted for readability.
// In logs/app.log
2023/10/28 | 14:30:05.123 | INFO | worker-process | Starting background job...
2023/10/28 | 14:30:05.500 | DEBUG | worker-process | Processing job.
~ {
~ "jobId": "xyz-123",
~ "payload": {
~ "user": "admin"
~ }
~ }
2023/10/28 | 14:30:05.900 | ERROR | worker-process | Job failed to complete.
Error: Task failed successfully
at Object.<anonymous> (/path/to/your/project/worker.ts:42:15)
at Module._compile (internal/modules/cjs/loader.js:1085:14)
...Core Concepts
LogManager: A static class that serves as the central point for configuration. You use it to register and configure your appenders.Logger: The object you use to write log messages. You get aLoggerinstance from theLogManager, usually with a specific namespace (e.g., a component or module name).Appenders: These are the destinations for your log messages.perfect-loggercomes with three built-in appenders:ConsoleAppender,FileAppender, andCallbackAppender.
Appenders
You can configure multiple appenders to send logs to different destinations simultaneously.
ConsoleAppender
Logs messages to the browser or Node.js console.
Example Usage
import { LogManager, ConsoleAppender, LogLevel } from 'perfect-logger';
LogManager.configure({
appenders: [
new ConsoleAppender({
minLevel: LogLevel.DEBUG,
format: '[{level}] {namespace} - {message}'
})
]
});
const logger = LogManager.getLogger('api-service');
logger.debug('Received a new request.');Configuration Options
| Option | Type | Default | Description |
| :--------- | :-------- |:------------------------------------------------------------| :------------------------------------------------------------------------------------------------------ |
| minLevel | LogLevel | LogLevel.INFO | The minimum log level this appender will process. |
| format | string | '{date} \| {time} \| {level} \| {namespace} \| {message}' | A template string that defines the log message format. See the "Formatting" section for placeholders. |
| timezone | string | undefined | An IANA timezone string (e.g., 'America/New_York') to use for {date} and {time}. Defaults to the system's timezone. |
FileAppender
Writes log messages to a file in a Node.js environment. This appender includes powerful log rotation features.
Example Usage
import { LogManager, FileAppender, LogLevel } from 'perfect-logger';
LogManager.configure({
appenders: [
new FileAppender({
logDirectory: 'logs',
fileName: 'app.log',
minLevel: LogLevel.INFO,
// Rotation settings
rotation: 'daily', // Rotate logs every day
maxSize: 10 * 1024 * 1024, // Rotate if file exceeds 10MB
maxFiles: 7 // Keep the last 7 rotated log files
})
]
});
const logger = LogManager.getLogger('worker-process');
logger.info('Starting background job...');Configuration Options
| Option | Type | Default | Description |
| :------------- | :------------------- |:-----------------------------------------------------------------------------------| :--------------------------------------------------------------------------------------------------------------------------------------- |
| minLevel | LogLevel | LogLevel.INFO | The minimum log level this appender will process. |
| logDirectory | string | 'logs' in the current working directory | The directory where log files will be stored. |
| fileName | string | 'app.log' | The name of the primary log file. |
| format | string | '{date} \| {time} \| {level} \| {namespace} \| {message}' | A template string for the log message format. |
| rotation | 'daily' | 'hourly' | undefined | The time-based rotation policy. A new file is created daily or hourly. |
| maxSize | number | null | The maximum size of a log file in bytes. If the file exceeds this size, it will be rotated. |
| maxFiles | number | null | The maximum number of archived log files to keep. The oldest files are deleted automatically. |
| timezone | string | undefined | An IANA timezone string to use for {date} and {time}. |
CallbackAppender
Provides a hook into the logging stream, executing a custom function for each log entry. This is perfect for integrating with third-party monitoring services, sending logs over a network, or performing any other custom logic.
Example Usage
import { LogManager, CallbackAppender, LogLevel, LogEntry } from 'perfect-logger';
// Example: Send critical errors to a monitoring service
function sendToMonitoringService(entry: LogEntry) {
const { level, message, error } = entry;
// In a real-world scenario, you would format and send this data
// to a service like Sentry, DataDog, etc.
console.log(`-- Sending to monitoring: [${LogLevel[level]}] ${message} --`);
if (error) {
console.log(error.stack);
}
}
LogManager.configure({
appenders: [
new CallbackAppender({
callback: sendToMonitoringService,
minLevel: LogLevel.ERROR // Only send errors and fatal logs
})
]
});
const logger = LogManager.getLogger('payment-gateway');
logger.info('Processing payment...'); // This will not trigger the callback
logger.fatal('Credit card service is down!', new Error('Service Unreachable'));Configuration Options
| Option | Type | Default | Description |
| :--------- | :------------ | :-------------- | :-------------------------------------------------------- |
| minLevel | LogLevel | LogLevel.TRACE | The minimum log level this appender will process. |
| callback | (entry: LogEntry) => void | Required | The function to execute for each log entry. |
Understanding Log Rotation Behavior
A key feature of the FileAppender is its predictable and convenient rotation strategy.
The Active Log File
When using size-based or hybrid (size and time) rotation, perfect-logger always writes to a file with a static name (e.g., app.log, as defined by fileName). This makes it incredibly easy to locate the logs for the currently running process. You can simply tail -f logs/app.log without needing to know the exact timestamp or rotation number.
The Rotation Process
When a rotation condition is met (either the file size exceeds maxSize or the time period for rotation elapses):
- The current active log file (e.g.,
app.log) is closed. - It is renamed to an archive file.
- On Size-Based Rotation: It gets a numeric suffix, like
app.log.1. Ifapp.log.1already exists, it is renamed toapp.log.2, and so on. - On Time-Based Rotation: It is renamed with a timestamp, like
app-2023-10-28.log.
- On Size-Based Rotation: It gets a numeric suffix, like
- A new, empty
app.logis created. - New log messages are written to this new file.
This process ensures that the active log file is always available at the same path, which is ideal for live monitoring.
Purely Time-Based Rotation
There is one exception to this behavior. If you configure only time-based rotation (e.g., rotation: 'daily') and do not set a maxSize, the logger will write directly to a time-stamped file (e.g., app-2023-10-28.log). A new file will be created automatically when the day or hour changes.
Advanced Configuration
Log Levels
Log levels are used to categorize the severity of a log message. When you set a minLevel on an appender, it will only process messages of that level or higher. The levels are ordered as follows:
TRACEDEBUGINFOWARNERRORFATAL
Formatting
You can control the output format of your logs using a template string with the following placeholders:
| Placeholder | Description |
| :------------ | :---------------------------------------------------------------------------- |
| {date} | The date of the log entry (e.g., 2023/10/28). |
| {time} | The time of the log entry, including milliseconds (e.g., 14:30:05.123). |
| {level} | The log level of the entry (e.g., INFO, WARN). |
| {namespace} | The namespace of the logger instance. |
| {message} | The main log message. |
| {context} | A stringified version of the optional context object passed to the logger. |
| {error} | The stack trace or message of an Error object passed to the logger. |
License
This project is licensed under the MIT License.
