@samofprog/nestjs-request-logger
v1.0.0
Published
A production-ready NestJS middleware for comprehensive HTTP request/response logging with security features
Maintainers
Readme
📡 NestJS Request Logger
A powerful, production-ready HTTP middleware for logging requests and responses in NestJS applications with enterprise-grade features.
Overview
NestJS Request Logger provides comprehensive HTTP request/response logging with:
- Security-First Design: Automatic masking of sensitive headers (authorization, cookies, API keys)
- High-Precision Timing: Nanosecond-accurate request duration measurement
- Framework Agnostic: Full support for Express and Fastify adapters
- Flexible Configuration: Customizable formatters, sanitizers, and path filters
- Production Ready: Zero external dependencies, extensive error handling, TypeScript support
Perfect for debugging, monitoring, auditing, and compliance requirements in enterprise NestJS applications.
✨ Features
| Feature | Description |
|------------------------------------------|----------------------------------------------------------------|
| 📥 Detailed request and response logging | Logs HTTP method, path, headers, status codes, and duration |
| 🔒 Sensitive header masking | Allows masking sensitive headers like Authorization or Cookie |
| 🚫 Path ignoring | Ignore logging on specific paths |
| 📝 Custom log message formatting | Customize incoming and completed request log messages |
| 🛠 Custom logger support | Use your own LoggerService or fallback to NestJS global logger |
| ⚠️ Log level distinction | Successful responses logged with log, errors with error |
| ⚙️ Framework compatibility | Works with both Express and Fastify |
| 🎛️ Configurable logging levels | Control what data to log: headers, request body, response data |
📦 Installation
Install the package using npm or yarn:
npm install @samofprog/nestjs-request-logger
# or
yarn add @samofprog/nestjs-request-logger🚀 Usage
Method 1: Using the Helper Function (Recommended)
Use the helper function in your NestJS bootstrap file:
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(requestLoggerFactory());
await app.listen(3000);
}
bootstrap();Method 2: Using Providers (Advanced)
For more advanced use cases with dependency injection:
import { createRequestLoggerProviders } from '@samofprog/nestjs-request-logger';
@Module({
providers: [
...createRequestLoggerProviders({
ignorePaths: ['/health'],
headerFields: ['content-type', 'authorization'],
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestLoggerMiddleware).forRoutes('*');
}
}⚙️ Usage with Custom Configuration
You can customize the middleware behavior with options:
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
app.use(requestLoggerFactory({
ignorePaths: ['/health', '/metrics'],
sensitiveHeaders: ['authorization', 'cookie'],
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
['authorization', 'cookie'].forEach(key => {
if (sanitized[key]) sanitized[key] = '[REDACTED]';
});
return sanitized;
},
incomingRequestMessage: (details) =>
`Incoming: ${details.method} ${details.url} → headers: ${JSON.stringify(details.headers)}`,
completedRequestMessage: (details) =>
`Completed: ${details.method} ${details.url} ← status ${details.statusCode} in ${details.durationMs} ms`,
}));🛠 Options
| Option | Type | Description | Default |
|---------------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-------------------------------|
| logger | LoggerService | Custom logger implementing NestJS LoggerService interface. | NestJS default logger |
| ignorePaths | string[] | List of URL paths to ignore from logging. | [] |
| sensitiveHeaders | string[] | List of header names to mask in logs (case-insensitive). | ['authorization', 'cookie', 'set-cookie', 'x-api-key'] |
| sanitizeHeaders | (headers: Record<string, any>) => Record<string, any> | Function to transform headers before logging (e.g., to mask values). | Identity function (no change) |
| incomingRequestMessage | (details) => string | Function returning the log message for incoming requests. Receives { method, url, headers, body }. | Default formatted string |
| completedRequestMessage | (details) => string | Function returning the log message for completed requests. Receives { method, url, statusCode, durationMs }. | Default formatted string |
| logRequestBody | boolean | Whether to include request body in the log messages. | false |
| headerFields | string[] | List of specific header fields to include in logs. | All headers |
🧩 Examples
🚫 Ignore paths and 🔒 mask sensitive headers
app.use(requestLoggerFactory({
ignorePaths: ['/health', '/metrics'],
sensitiveHeaders: ['authorization', 'cookie'],
}));🧼 Custom sanitization of headers
app.use(requestLoggerFactory({
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
if (sanitized['authorization']) sanitized['authorization'] = '[TOKEN REDACTED]';
if (sanitized['cookie']) sanitized['cookie'] = '[COOKIE REDACTED]';
return sanitized;
}
}));🎛️ Configure logging levels
app.use(requestLoggerFactory({
logRequestBody: true, // Include request body in logs (default: false)
headerFields: ['content-type', 'authorization'], // Specific headers to log
}));🛠 Custom logger
import { Logger } from '@nestjs/common';
const customLogger = new Logger('MyCustomLogger');
app.use(requestLoggerFactory({ logger: customLogger }));🔧 Default Sensitive Headers
By default, the following headers are automatically masked:
const DEFAULT_SENSITIVE_HEADERS = [
'authorization',
'cookie',
'set-cookie',
'x-api-key'
];📋 Default Log Message Format
The middleware formats log messages in a structured key=value format with descriptive prefixes for easy parsing and readability:
Incoming Request:
Incoming request: method=GET path=/api/users content-type=application/jsonCompleted Request:
Request completed: method=GET path=/api/users statusCode=200 durationMs=45.23msThis format provides:
- Clear prefixes identifying request lifecycle
- Key=value pairs for easy parsing by log aggregation tools
- camelCase keys following Node.js conventions
- Structured data that's human-readable and machine-parseable
📊 Custom Message Formatters
Format log messages to match your requirements:
app.use(requestLoggerFactory({
incomingRequestMessage: (details) => {
const timestamp = new Date().toISOString();
return `[${timestamp}] → ${details.method} ${details.url}`;
},
completedRequestMessage: (details) => {
const logLevel = details.statusCode >= 400 ? '❌ ERROR' : '✅ SUCCESS';
return `[${logLevel}] ← ${details.method} ${details.url} | Status: ${details.statusCode} | Duration: ${details.durationMs}ms`;
},
}));🔗 With ConfigService (NestJS Config)
Load configuration from environment variables:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { requestLoggerFactory } from '@samofprog/nestjs-request-logger';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule implements NestModule {
constructor(private configService: ConfigService) {}
configure(consumer: MiddlewareConsumer) {
const headerFields = this.configService.get('HEADER_FIELDS', '').split(',').filter(Boolean);
const logBody = this.configService.get('LOG_REQUEST_BODY') === 'true';
const ignorePaths = this.configService.get('IGNORE_PATHS', '/health,/metrics').split(',');
consumer
.apply(requestLoggerFactory({
headerFields,
logRequestBody: logBody,
ignorePaths,
}))
.forRoutes('*');
}
}🎯 Path Matching Patterns
The middleware supports multiple path matching strategies:
app.use(requestLoggerFactory({
ignorePaths: [
// Exact substring match
'/health',
'/metrics',
'/swagger',
// Wildcard patterns (converted to regex)
'/static/*',
'/api/internal/*',
'/uploads/*',
// Complex regex patterns
'/api/v[0-9]+/public',
'/temp/.*\\.tmp',
],
}));🔐 Advanced Security Configuration
Implement granular control over sensitive data:
app.use(requestLoggerFactory({
sensitiveHeaders: [
'authorization',
'cookie',
'set-cookie',
'x-api-key',
'x-auth-token',
'x-custom-secret',
],
sanitizeHeaders: (headers) => {
const sanitized = { ...headers };
// Partial masking for tokens
if (sanitized['authorization']) {
const auth = sanitized['authorization'];
sanitized['authorization'] = typeof auth === 'string'
? `${auth.substring(0, 7)}...${auth.substring(auth.length - 10)}`
: auth;
}
// Mask other sensitive headers completely
['cookie', 'x-api-key', 'x-auth-token'].forEach(key => {
if (sanitized[key]) sanitized[key] = '*'.repeat(20);
});
return sanitized;
},
}));📈 Performance Monitoring
Combine with performance analysis:
app.use(requestLoggerFactory({
completedRequestMessage: (details) => {
const duration = parseFloat(details.durationMs);
let performanceLevel = '🟢';
if (duration > 1000) performanceLevel = '🔴';
else if (duration > 500) performanceLevel = '🟡';
else if (duration > 100) performanceLevel = '🟡';
return `${performanceLevel} ${details.method} ${details.url} - ${details.statusCode} in ${details.durationMs}ms`;
},
}));📋 Requirements
- Node.js: 14.0.0 or higher
- NestJS: 8.0.0 or higher
- Express: 4.0.0 or higher (for Express adapter)
- Fastify: 4.0.0 or 5.0.0 (for Fastify adapter)
🔄 Version Compatibility
| NestJS Version | Package Support | |---|---| | 8.x | ✅ Full Support | | 9.x | ✅ Full Support | | 10.x | ✅ Full Support | | 11.x | ✅ Full Support |
🐛 Troubleshooting
Headers not being logged
Ensure headerFields is set in your configuration:
app.use(requestLoggerFactory({ headerFields: ['content-type', 'authorization'] }));Custom logger not being used
Make sure your custom logger implements the NestJS LoggerService interface:
import { LoggerService } from '@nestjs/common';
export class MyLogger implements LoggerService {
log(message: string) { /* ... */ }
error(message: string, trace?: string) { /* ... */ }
warn(message: string) { /* ... */ }
debug(message: string) { /* ... */ }
verbose(message: string) { /* ... */ }
}Performance impact on high-traffic applications
The middleware is optimized for production use. If you experience performance issues:
- Use
ignorePathsto exclude high-frequency endpoints (health checks, metrics) - Consider disabling
headerFieldsandlogRequestBodyif not needed - Use an async logger that doesn't block the request
📝 API Reference
requestLoggerFactory(options?)
Creates and returns an Express/Fastify compatible middleware function.
Parameters:
options(optional):Partial<RequestLoggerOptions>
Returns: (req: Req, res: Res, next: Function) => void
Example:
const middleware = requestLoggerFactory({ headerFields: ['content-type', 'authorization'] });
app.use(middleware);createRequestLoggerProviders(options?)
Creates NestJS providers for dependency injection.
Parameters:
options(optional):RequestLoggerModuleOptions
Returns: Provider[]
createRequestLoggerAsyncProviders(options)
Creates async NestJS providers for dynamic configuration.
Parameters:
options:RequestLoggerAsyncOptionswithuseFactoryandinject
Returns: Provider[]
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📧 Support
For issues, questions, or suggestions:
- Open an issue on GitHub
- Check existing documentation and examples
📄 License
This package is open-source and available under the MIT License.
Copyright (c) 2024 samofprog
🙏 Acknowledgments
Built with ❤️ for the NestJS community. Special thanks to all contributors and users who have provided feedback and improvements.
