UNPKG

perfect-logger

Version:

A zero-dependency, isomorphic logger for Node.js and Browsers with plugin support.

261 lines (191 loc) 13 kB
# 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 `FileAppender` are Node.js-only). - **Zero Dependencies**: A lightweight library with no external dependencies. ## Installation ```bash npm install perfect-logger ``` ## Quick Start Get up and running with a few lines of code. ```typescript 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 minLevel ``` ## Example 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 a `Logger` instance from the `LogManager`, usually with a specific namespace (e.g., a component or module name). - **`Appenders`**: These are the destinations for your log messages. `perfect-logger` comes with three built-in appenders: `ConsoleAppender`, `FileAppender`, and `CallbackAppender`. --- ## 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 ```typescript 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 ```typescript 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 ```typescript 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): 1. The current active log file (e.g., `app.log`) is closed. 2. It is renamed to an archive file. * **On Size-Based Rotation**: It gets a numeric suffix, like `app.log.1`. If `app.log.1` already exists, it is renamed to `app.log.2`, and so on. * **On Time-Based Rotation**: It is renamed with a timestamp, like `app-2023-10-28.log`. 3. A new, empty `app.log` is created. 4. 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: - `TRACE` - `DEBUG` - `INFO` - `WARN` - `ERROR` - `FATAL` ### 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.