@heavybit/logger-ts
v1.0.0
Published
A comprehensive TypeScript logging package for Express.js with AOP support, request/response logging middleware, stdout and file logging
Downloads
184
Maintainers
Readme
@pesalink/logger-ts
A comprehensive TypeScript logging package for Express.js applications with AOP (Aspect-Oriented Programming) support, request/response logging middleware, stdout and file logging based on environment configuration.
Features
- 🚀 TypeScript First: Full TypeScript support with comprehensive type definitions
- 📝 Winston-based: Built on the robust Winston logging library
- 🔄 Dual Output: Log to stdout (console), files, or both based on configuration
- 🗂️ Log Rotation: Automatic daily log rotation with compression
- 🎯 AOP Logging: Aspect-oriented programming support with decorators and utilities
- 🛡️ Data Sanitization: Automatic redaction of sensitive fields (passwords, tokens, etc.)
- 🏷️ Class-based Log Levels: Different log levels for controllers, services, repositories, etc.
- 🔧 Environment Config: Full configuration via environment variables
- 🧩 Express Middleware: Ready-to-use logging middleware for Express.js
- 📊 Structured Logging: JSON-formatted logs for easy parsing and analysis
Installation
npm install @pesalink/logger-ts
# Peer dependency
npm install expressQuick Start
Basic Usage
import express from 'express';
import { initLogger, getLogger, loggingMiddleware } from '@pesalink/logger-ts';
const app = express();
// Initialize logger (reads from environment variables)
const logger = initLogger();
// Add middleware
app.use(express.json());
app.use(loggingMiddleware);
// Use the logger
app.get('/api/users', async (req, res) => {
logger.info('Fetching users');
res.json({ users: [] });
});
app.listen(3000, () => {
logger.info('Server started on port 3000');
});Custom Configuration
import { initLogger, LoggerConfig } from '@pesalink/logger-ts';
const config: Partial<LoggerConfig> = {
target: 'both', // 'stdout' | 'file' | 'both'
defaultLevel: 'debug',
appName: 'my-service',
console: {
colorize: true,
level: 'debug',
},
file: {
directory: 'logs',
maxSize: '100m',
maxFiles: '30d',
zippedArchive: true,
datePattern: 'YYYY-MM-DD',
level: 'info',
},
classLevels: {
controllers: 'info',
services: 'debug',
repositories: 'error',
},
sensitiveFields: ['customSecret'], // Additional fields to redact
};
const logger = initLogger(config);Environment Variables
# Log target: 'stdout', 'file', or 'both'
LOG_TARGET=both
# Default log level
LOG_LEVEL=debug
# Console configuration
LOG_COLORIZE=true
# File configuration
LOG_DIR=logs
LOG_MAX_SIZE=50m
LOG_MAX_FILES=30d
LOG_DATE_PATTERN=YYYY-MM-DD
LOG_ZIPPED_ARCHIVE=true
# Class-specific log levels
LOG_LEVEL_CONTROLLERS=info
LOG_LEVEL_SERVICES=debug
LOG_LEVEL_REPOSITORIES=error
LOG_LEVEL_UTILS=warn
LOG_LEVEL_MIDDLEWARE=info
LOG_LEVEL_ROUTES=info
# Application info
APP_NAME=my-app
NODE_ENV=development
HEALTHCHECK_ENDPOINT=/healthAPI Reference
Logger Functions
initLogger(config?: Partial<LoggerConfig>): ExtendedLogger
Initialize the logger singleton. Call once at application startup.
const logger = initLogger({
target: 'both',
defaultLevel: 'info',
});getLogger(): ExtendedLogger
Get the logger instance (creates default if not initialized).
const logger = getLogger();
logger.info('Hello world');
logger.error('An error occurred', { error: err.message });createLogger(config?: Partial<LoggerConfig>): ExtendedLogger
Create a new logger instance with custom config (does not replace singleton).
const customLogger = createLogger({
target: 'file',
file: { directory: 'custom-logs' },
});Logging Methods
const logger = getLogger();
// Standard Winston methods
logger.error('Error message', { metadata: 'value' });
logger.warn('Warning message');
logger.info('Info message');
logger.debug('Debug message');
logger.verbose('Verbose message');
// Extended methods
logger.logWithMeta('info', 'Message', { custom: 'data' });
logger.logRequest({
message: 'Request completed',
method: 'GET',
url: '/api/users',
statusCode: 200,
responseTime: '50ms',
});Middleware
loggingMiddleware
Default logging middleware with standard configuration.
app.use(loggingMiddleware);createLoggingMiddleware(options: MiddlewareOptions)
Create custom logging middleware.
app.use(createLoggingMiddleware({
skipEndpoints: ['/health', '/metrics'],
logRequestBody: true,
logResponseBody: false,
requestIdGenerator: () => uuid(),
additionalFields: (req) => ({
userId: req.user?.id,
}),
}));errorLoggingMiddleware
Error logging middleware for Express error handling.
app.use(errorLoggingMiddleware);morganStyleMiddleware(format)
Morgan-style request logging.
app.use(morganStyleMiddleware('dev')); // 'combined' | 'common' | 'dev' | 'short' | 'tiny'AOP (Aspect-Oriented Programming)
Using Decorators
import { LogClass, LogMethod } from '@pesalink/logger-ts';
@LogClass(['constructor'], ['getAll']) // Exclude constructor, only log errors for getAll
class UserController {
async getAll(req, res) {
// Only errors will be logged
}
@LogMethod('UserController', 'create', 'around', 'controllers')
async create(req, res) {
// Full before/after logging
}
}Using logMethodsWithAOP
import { logMethodsWithAOP } from '@pesalink/logger-ts';
class ProductService {
async getAll(req, res) { /* ... */ }
async create(req, res) { /* ... */ }
}
logMethodsWithAOP(
ProductService.prototype,
['constructor'], // Excluded methods
['getAll'], // Only log errors for these
'ProductService', // Class name
'services', // Class type
'around' // Advice type
);Using withLogging Wrapper
import { withLogging } from '@pesalink/logger-ts';
const handler = async (req, res) => {
res.json({ data: [] });
};
app.get('/api/items', withLogging(handler, 'getItems', 'ItemController'));Using wrapRoutesWithLogging
import { Router } from 'express';
import { wrapRoutesWithLogging } from '@pesalink/logger-ts';
const router = Router();
router.get('/', handler1);
router.post('/', handler2);
// Wrap all routes in the router
wrapRoutesWithLogging(router, 'ApiRouter', 'routes', ['GET /']);
app.use('/api', router);Sanitization
import { sanitizeData, sanitizeHeaders, prepareForLogging } from '@pesalink/logger-ts';
// Sanitize object (redacts sensitive fields)
const safe = sanitizeData({
username: 'john',
password: 'secret123', // -> [REDACTED]
token: 'abc123', // -> [REDACTED]
});
// Sanitize headers
const safeHeaders = sanitizeHeaders(req.headers);
// Prepare for logging with truncation
const truncated = prepareForLogging(largeObject, {
truncateLength: 1000,
});Configuration Functions
import {
initConfig,
getConfig,
resetConfig,
getLogLevelForClass,
} from '@pesalink/logger-ts';
// Initialize configuration
initConfig({ target: 'both' });
// Get current config
const config = getConfig();
// Get log level for a class type
const level = getLogLevelForClass(config, 'controllers'); // 'info'
// Reset configuration (useful for testing)
resetConfig();Log Output Format
Console (Structured)
2024-01-15T10:30:45.123+0000 | app=my-service | className=UserController | OperationName=getUser | Method=GET | Endpoint=/api/users/1 | LogLevel=info | Payload={} | Message=Request completed | RequestId=abc-123 | ResponseTime=45ms | StatusCode=200File (JSON)
{
"timestamp": "2024-01-15T10:30:45.123+0000",
"level": "info",
"app": "my-service",
"environment": "production",
"className": "UserController",
"methodName": "getUser",
"method": "GET",
"url": "/api/users/1",
"requestId": "abc-123",
"statusCode": 200,
"responseTime": "45ms",
"message": "Request completed"
}Log Files
When file logging is enabled, logs are stored in the configured directory with the following structure:
logs/
├── 2024-01-15.log # All logs for the day
├── 2024-01-15-error.log # Error-only logs
├── 2024-01-14.log.gz # Compressed archived logs
└── 2024-01-14-error.log.gzBest Practices
1. Initialize Early
// At the top of your entry file
import { initLogger } from '@pesalink/logger-ts';
const logger = initLogger();2. Use Appropriate Log Levels
logger.error('Database connection failed', { error: err.message }); // Errors
logger.warn('Rate limit approaching', { current: 90, max: 100 }); // Warnings
logger.info('User logged in', { userId: '123' }); // Info
logger.debug('Cache hit', { key: 'user:123' }); // Debug3. Include Context
logger.info('Processing payment', {
transactionId: 'txn-123',
userId: 'user-456',
amount: 100.00,
currency: 'USD',
});4. Handle Errors Properly
try {
await riskyOperation();
} catch (error) {
logger.error('Operation failed', {
error: error.message,
stack: error.stack,
context: { operationId: '123' },
});
throw error;
}5. Use Class-Specific Levels
Configure verbose logging for debugging specific layers:
LOG_LEVEL_SERVICES=debug
LOG_LEVEL_REPOSITORIES=error # Only log database errorsExamples
See the examples/ directory for complete examples:
- basic-usage.ts - Basic Express setup
- aop-usage.ts - AOP decorators and utilities
- external-service.ts - Microservice integration
TypeScript Support
All types are exported from the package:
import type {
LoggerConfig,
LogLevel,
ClassType,
LogTarget,
ExtendedLogger,
RequestLogEntry,
AOPContext,
MiddlewareOptions,
} from '@pesalink/logger-ts';License
MIT
Author
Nicholas Kute
