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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brmorillo/logger

v2.1.1

Published

A flexible and extensible logging library with file persistence, rotation, and compression - supports Pino, Winston, and Console

Readme

@brmorillo/logger

A flexible and extensible logging library for TypeScript/JavaScript applications, supporting multiple logger implementations (Pino, Winston, Console) with automatic file persistence, rotation, and compression.

npm version License: MIT TypeScript

Note: This library is now licensed under the permissive MIT License, allowing you to use it freely in any project, including commercial applications.

🚀 Quick Start

bun add @brmorillo/logger
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',
  level: 'info',
  prettyPrint: true,
});

logger.info('Hello, world!', { user: 'Bruno' });

With File Persistence

import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',
  level: 'info',
  fileTransport: {
    logDir: './logs',
    rotationInterval: 'hourly',
    maxRetentionDays: 7,
    compress: true,
  },
});

logger.info('Logs saved to console AND files!');

✨ Features

🚀 High Performance

  • Pino Logger Included: High-performance JSON logging pre-installed
  • Zero Dependencies: Pino comes bundled, Winston is optional
  • Fast & Lightweight: Minimal overhead for production workloads

📁 File Persistence

  • Automatic Rotation: Hourly or daily log file rotation
  • Compression: Auto-compress old logs to .log.gz (saves ~85% space)
  • Smart Cleanup: Configurable retention policy (e.g., keep 7 days)
  • Dual Output: Write to console AND files simultaneously

🔧 Developer Experience

  • Simple API: Unified interface for all logger types
  • TypeScript First: Full type safety with comprehensive types
  • Multiple Loggers: Pino (default), Winston (optional), Console
  • *📦 Installation
# Bun (recommended)
bun add @brmorillo/logger

# npm
npm install @brmorillo/logger

# yarn
yarn add @brmorillo/logger

# pnpm
pnpm add @brmorillo/logger

Optional: Install Winston only if needed

bun add winston

Note: Pino comes pre-installed. Winston is only needed for advanced transport features.ort { LogService } from '@brmorillo/logger';

// Get logger instance with default configuration (Pino) const logger = LogService.getInstance();

// Log messages logger.info('Application started'); logger.warn('This is a warning'); logger.error('An error occurred', { error: new Error('Test') }); logger.debug('Debug information', { userId: 123 });


## Configuration

### Basic Configuration

```typescript
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',        // 'pino' | 'winston' | 'console'
  level: 'info',       // 'error' | 'warn' | 'info' | 'debug'
  prettyPrint: true,   // Enable formatted output
});

File Persistence Configuration

Enable automatic file persistence with rotation and compression:

imp⚙️ Configuration

### Basic Setup

```typescript
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',        // 'pino' | 'winston' | 'console'
  level: 'info',       // 'error' | 'warn' | 'info' | 'debug'
  prettyPrint: true,   // Formatted output (dev only)
});

Production Setup (with File Persistence)

import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',
  level: 'info',
  prettyPrint: false,
  fileTransport: {
    logDir: './logs',
    rotationInterval: 'hourly',  // or 'daily'
    maxRetentionDays: 7,
    compress: true,
    fileNamePattern: 'app',      // app-2025-12-21-14.log
  },
});

File Structure Example:

logs/
├── app-2025-12-21-14.log       # Current hour (active)
├── app-2025-12-21-13.log.gz    # Compressed
├── app-2025-12-21-12.log.gz
└── app-2025-12-21-11.log.gz

Environment-Based Configuration

const isProd = process.env.NODE_ENV === 'production';

const logger = LogService.getInstance({
  type: 'pino',
  level: process.env.LOG_LEVEL || 'info',
  prettyPrint: !isProd,
  fileTransport: isProd ? {
    logDir: './logs',
    rotationInterval: 'hourly',
    maxRetentionDays: 7,
    compress: true,
  } : undefined,
});

FileTransport Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | logDir | string | required | Directory to store log files | | rotationInterval | 'hourly' \| 'daily' | 'hourly' | Rotation frequency | | maxRetentionDays | number | 7 | Days to keep logs | | compress | boolean | true | Gzip compression | | fileNamePattern | string | 'app' | File name prefix |

logger.info('User action', { 
  userId: '12345', 
  action: 'purchase', 
  amount: 99.99 
});

logger.error('Payment failed', { 
  orderId: 'ORD-001', 
  error: error.message,
  stack: error.stack 
});

Express.js Integration

imp📝 Usage Examples

### Basic Logging

```typescript
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance();

logger.info('User logged in');
logger.warn('High memory usage detected');
logger.error('Failed to connect to database');
logger.debug('Request details', { method: 'GET', path: '/api/users' });

W

});

app.get('/', (req, res) => { logger.debug('Home route accessed'); res.send('Hello World!'); });


### NestJS Integration
Custom Logger

```typescript
import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common';
import { LogService } from '@brmorillo/logger';

@Injectable()
export class LoggerService implements NestLoggerService {
  private logger = LogService.getInstance({
    type: 'pino',
    level: 'info',
    prettyPrint: process.env.NODE_ENV !== 'production',
    fileTransport: process.env.NODE_ENV === 'production' ? {
      logDir: './logs'
app.use((req, res, next) => {
  logger.info('Request', { method: req.method, path: req.path, ip: req.ip error(message: string, trace?: string, context?: string) {
    this.logger.error(message, { trace, context });
  }

  warn(message: string, context?: string) {
    this.logger.warn(message, { context });
  }

  debug(message: string, context?: string) {
    this.logger.debug(message, { context });
  }
}

Fastify Integration

import Fastify from 'fastify';
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({
  type: 'pino',
  level: 'info',
});

const fastifHook

```typescript
import Fastify from 'fastify';
import { LogService } from '@brmorillo/logger';

const logger = LogService.getInstance({ type: 'pino', level: 'info' });
const fastify = Fastify();

fastify.addHook('onRequest', (request, reply, done) => {
  logger.info('Request', { method: request.method, url: request.url });
  done();
});

fastify.get('/', async () => {
  logger.debug('Hof LogService.

```typescript
const logger = LogService.getInstance({
  type: 'pino',
  level: 'info',
  prettyPrint: true,
});

configure(options: LoggerOptions): void

📚 API Reference

LogService Methods

// Get singleton instance
const logger = LogService.getInstance(options?: LoggerOptions);

// Reconfigure at runtime
logger.configure(options: LoggerOptions);

// Logging methods
logger.info(message: string, ...meta: any[]);
logger.warn(message: string, ...meta: any[]);
logger.error(message: string, ...meta: any[]);
logger.debug(message: string, ...meta: any[]);

LoggerOptions Interface

interface LoggerOptions {
  type?: 'pino' | 'winston' | 'console';         // Default: 'pino'
  level?: 'error' | 'warn' | 'info' | 'debug';   // Default: 'info'
  prettyPrint?: boolean;                          // Default: false
  fileTransport?: FileTransportConfig;            // Optional
}

interface FileTransportConfig {
  logDir: string;                                 // Required
  rotationInterval?: 'hourly' | 'daily';         // Default: 'hourly'
  maxRetentionDays?: number;                     // Default: 7
  compress?: boolean;                             // Default: true
  fileNamePattern?: string;                       // Default: 'app'
}st BUCKET_NAME = 'my-app-logs';

async function uploadLogs() {
  const files = await readdir(LOG_DIR);
  
  // Upload only .log.gz files (compressed logs)
  for (const file of files) {
    if (file.endsWith('.log.gz')) {
      const filePath = join(LOG_DIR, file);
      const fileContent = await readFile(filePath);
      
      await s3Client.send(
        new PutObjectCommand({
          Bucket: BUCKET_NAME,
          Key: `logs/${file}`,
          Body: fileContent,
          ContentType: 'application/gzip',
        })
      );
      
      // Delete local file after successful upload
      await unlink(filePath);
      console.log(`Uploaded and deleted: ${file}`);
    }
  }
}

uploadLogs().catch(console.error);

Schedule with cron to run every hour:

# Crontab: Run 5 minutes after each hour
5 * * * * cd /path/to/app && bun run scripts/upload-logs.ts

Performance Considerations

  • Pino: Fastest logger, recommended for production. Uses JSON format by default.
  • Winston: Feature-rich, slightly slower than Pino. Good for complex logging needs.
  • Console: Simple and lightweight, best for development or simple applications.

Compatibility

  • ✅ Node.js >= 18
  • ✅ Bun >= 1.0
  • ✅ TypeScript >= 5.0
  • ✅🚀 Advanced Usage

Upload Logs to S3

// scripts/upload-logs.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { readdir, readFile, unlink } from 'fs/promises';

const s3 = new S3Client({ region: 'us-east-1' });

async function uploadLogs() {
  const files = await readdir('./logs');
  
  for (const file of files) {
    if (file.endsWith('.log.gz')) {
      const content = await readFile(`./logs/${file}`);
      
      await s3.send(new PutObjectCommand({
        Bucket: 'my-app-logs',
        Key: `logs/${file}`,
        Body: content,
        ContentType: 'application/gzip',
      }));
      
      await unlink(`./logs/${file}`);
      console.log(`Uploaded: ${file}`);
    }
  }
}

uploadLogs();

Schedule with cron:

5 * * * * cd /app && bun run scripts/upload-logs.ts

Performance Tips

| Logger | Performance | Use Case | |--------|-------------|----------| | Pino | ⚡ Fastest | Production, high-throughput | | Winston | 🚀 Fast | Complex transports, legacy | | Console | 💻 Simple | Development, debugging |📋 Requirements

  • Node.js >= 18
  • Bun >= 1.0
  • TypeScript >= 5.0

Supports both ES Modules and CommonJS.

📖 Documentation

🤝 Contributing

Contributions welcome! Please submit a Pull Request.

📄 License

MIT © Bruno Morillo

🔗 Links


Made with ❤️ by Bruno Morillo