0.0.1 • Published 6 months ago

@smmorshed/server-utils v0.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
6 months ago

Server Utils

CI npm bundle size npm version

A professional utility package for Express.js-based Node servers, written in TypeScript with modern best practices.

Features

  • 📦 Dual ESM + CommonJS support
  • 🛠️ Tree-shakable (only import what you need)
  • 💪 Fully typed with TypeScript
  • ✅ Comprehensive test coverage

Installation

npm install @smmorshed/server-utils

Utilities

Logger

A lightweight, customizable logger for Node.js applications with multiple transport options.

Features

  • Multiple Log Levels: debug, info, warn, error
  • Multiple Transports: console, file, memory
  • Timestamp Support: automatic or custom timestamp formats
  • Context Support: attach persistent contextual data to logs
  • Asynchronous Logging: non-blocking log operations with buffering
  • Customizable Formatters: fully control log output format
  • Tree-shakable: only import the features you need

Basic Usage

import { Logger } from '@smmorshed/server-utils';

// Create with default settings
const logger = new Logger();

// Basic usage
logger.info('Server started on port 3000');
logger.warn('Memory usage high', { memoryUsage: process.memoryUsage() });
logger.error('Database connection failed', { retryCount: 3 });

// Change log level at runtime
logger.setLevel('debug');

Advanced Configuration

import { Logger } from '@smmorshed/server-utils';

// Configure with options
const customLogger = new Logger({
  level: 'debug', // 'debug' | 'info' | 'warn' | 'error'
  timestamp: true, // include timestamps in logs
  formatter: (level, message, meta) => {
    // Custom formatter
    return `${level.toUpperCase()}: ${message} ${meta ? JSON.stringify(meta) : ''}`;
  },
  context: { // Persistent context data for all logs 
    service: 'payment-service',
    version: '1.0.0'
  },
  transports: [
    { type: 'console', colors: true },
    { type: 'file', filePath: 'logs/app.log', maxSize: 10485760, maxFiles: 5 }
  ],
  async: true, // Use non-blocking logging
  bufferSize: 100, // Max number of logs to buffer before flushing
  flushInterval: 1000 // Flush interval in milliseconds
});

Using Multiple Transports

import { Logger } from '@smmorshed/server-utils';

const multiTransportLogger = new Logger({
  // Use multiple transports
  transports: [
    { type: 'console' }, // Console output
    { type: 'file', filePath: 'logs/app.log' }, // File output
    { type: 'memory', maxEntries: 1000 } // In-memory storage (useful for testing)
  ]
});

multiTransportLogger.info('This message goes to all transports');

Context Support for Request Tracking

import { Logger } from '@smmorshed/server-utils';
import { v4 as uuidv4 } from 'uuid';

// Create a base logger
const baseLogger = new Logger();

// In an Express middleware
app.use((req, res, next) => {
  const requestId = uuidv4();
  
  // Create a logger with context for this request
  req.logger = baseLogger.withContext({
    requestId,
    path: req.path,
    method: req.method,
    ip: req.ip
  });
  
  next();
});

// Later in a route handler
app.get('/users', (req, res) => {
  req.logger.info('Getting users', { query: req.query });
  // Log will include requestId, path, method, and ip automatically
  
  // ...process request
});

Express.js Application Example

import express from 'express';
import { Logger } from '@smmorshed/server-utils';

const app = express();
const logger = new Logger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug' });

// Request logging middleware
app.use((req, res, next) => {
  const start = Date.now();

  logger.debug('Request received', {
    method: req.method,
    path: req.path,
    query: req.query,
  });

  res.on('finish', () => {
    const duration = Date.now() - start;
    const logLevel = res.statusCode >= 400 ? 'error' : 'info';

    logger[logLevel]('Response sent', {
      method: req.method,
      path: req.path,
      statusCode: res.statusCode,
      duration: `${duration}ms`,
    });
  });

  next();
});

// Route handlers
app.get('/api/users', (req, res) => {
  try {
    logger.debug('Fetching users');
    // Database operations...
    const users = [{ id: 1, name: 'John' }];

    logger.info('Users fetched successfully', { count: users.length });
    res.json(users);
  } catch (error) {
    logger.error('Failed to fetch users', {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined
    });
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Server startup
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  logger.info(`Server started`, { port: PORT, env: process.env.NODE_ENV });
});

Structured JSON Logging

import { Logger } from '@smmorshed/server-utils';

// Create a JSON formatter for structured logging
const logger = new Logger({
  formatter: (level, message, meta) => {
    const logEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      ...meta,
      service: 'payment-api',
      environment: process.env.NODE_ENV,
    };
    return JSON.stringify(logEntry);
  },
});

// Now logs will be in JSON format for easy parsing by log aggregation tools
logger.info('Payment processed', {
  paymentId: '12345',
  amount: 99.99,
  currency: 'USD',
  userId: 'user_abc123',
});

// Output: {"timestamp":"2023-04-16T12:34:56.789Z","level":"info","message":"Payment processed","paymentId":"12345","amount":99.99,"currency":"USD","userId":"user_abc123","service":"payment-api","environment":"production"}

Asynchronous Logging

import { Logger } from '@smmorshed/server-utils';

// Create logger with async logging enabled
const asyncLogger = new Logger({
  async: true,
  bufferSize: 50,      // Flush after 50 log entries
  flushInterval: 2000  // Or flush every 2 seconds
});

// These logs will be buffered and won't block the main thread
for (let i = 0; i < 1000; i++) {
  asyncLogger.info(`Processing item ${i}`);
}

// Make sure to close the logger when your application exits
process.on('SIGTERM', () => {
  asyncLogger.close();  // This will flush any remaining logs
  process.exit(0);
});

API Documentation

Logger

A class that provides leveled logging functionality with customizable formatting and multiple transports.

Constructor

constructor(options?: LoggerOptions)

Parameters:

  • options (optional): Configuration options for the logger

LoggerOptions Interface

interface LoggerOptions {
  level?: LogLevel;           // Minimum log level ('debug', 'info', 'warn', 'error')
  timestamp?: boolean;        // Whether to include timestamps in logs
  formatter?: (level: LogLevel, message: string, meta?: Record<string, unknown>) => string;
  context?: Record<string, unknown>;  // Context data for all logs
  transports?: TransportConfig[];     // Where logs should be sent
  async?: boolean;            // Whether logging should be asynchronous
  bufferSize?: number;        // Buffer size for async logging
  flushInterval?: number;     // Flush interval in ms for async logging
}

Transport Types

The Logger supports the following transport configurations:

// Console Transport
{ 
  type: 'console',
  colors?: boolean       // Whether to use colors (not implemented yet)
}

// File Transport
{
  type: 'file',
  filePath: string,      // Path to log file
  maxSize?: number,      // Max file size before rotation (default: 10MB)
  maxFiles?: number      // Max number of files to keep (default: 5)
}

// Memory Transport (useful for testing)
{
  type: 'memory',
  maxEntries?: number    // Max log entries to keep in memory (default: 1000)
}

Methods

debug(message, meta?)
debug(message: string, meta?: Record<string, unknown>): void

Logs a debug message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
info(message, meta?)
info(message: string, meta?: Record<string, unknown>): void

Logs an info message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
warn(message, meta?)
warn(message: string, meta?: Record<string, unknown>): void

Logs a warning message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
error(message, meta?)
error(message: string, meta?: Record<string, unknown>): void

Logs an error message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
withContext(context)
withContext(context: Record<string, unknown>): Logger

Creates a new logger with the provided context merged with the existing logger's context.

Parameters:

  • context: Context data to include with all logs from the new logger
  • Returns: A new Logger instance with the combined context
setLevel(level)
setLevel(level: LogLevel): void

Sets the minimum log level.

Parameters:

  • level: The new minimum log level ('debug', 'info', 'warn', or 'error')
getLevel()
getLevel(): LogLevel

Gets the current log level.

Returns: The current log level

flush()
flush(): void

Flushes any buffered log entries. Mainly useful with asynchronous logging.

close()
close(): void

Closes the logger, releasing any resources and flushing any buffered logs.

Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Build the package
npm run build

# Generate API documentation
npm run docs

License

MIT

0.0.1

6 months ago

0.1.0

6 months ago