npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

perfect-logger

v3.0.0

Published

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

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 FileAppender are Node.js-only).
  • Zero Dependencies: A lightweight library with no external dependencies.

Installation

npm install perfect-logger

Quick 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 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

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):

  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.