@globalart/nestjs-logger
Professional logging module for NestJS with clean architecture, multiple formats, and built-in security features.
Installation
npm install @globalart/nestjs-logger
Overview
This package provides a comprehensive logging solution for NestJS applications with support for multiple output formats, automatic HTTP request logging, and built-in security features. Built on Clean Architecture principles, it offers excellent performance and extensibility.
Key Features
- Clean Architecture - Separation of concerns with SOLID principles
- Type Safety - Full TypeScript support with IntelliSense
- Performance Optimized - Minimal allocations and efficient queries
- Multiple Formats - Text, JSON, and Pino output support
- HTTP Logging - Automatic request/response logging
- Security - Automatic sanitization of sensitive data
- Extensible - Easy to add custom formatters and transports
Quick Start
import { Module } from "@nestjs/common";
import { LoggerModule } from "@globalart/nestjs-logger";
@Module({
imports: [
LoggerModule.forRoot({
level: "info",
timestamp: true,
colors: true,
format: "text",
}),
],
})
export class AppModule {}
Configuration
Basic Configuration
import { RequestMethod } from "@nestjs/common";
LoggerModule.forRoot({
level: "debug",
timestamp: true,
colors: true,
format: "json",
sensitiveFields: ["password", "secret"],
exclude: [
{ method: RequestMethod.GET, path: "/health" },
{ method: RequestMethod.ALL, path: "/favicon.ico" },
],
});
Async Configuration
import { ConfigService } from "@nestjs/config";
LoggerModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
level: configService.get("LOG_LEVEL", "info"),
format: configService.get("LOG_FORMAT", "text"),
colors: !configService.get("PRODUCTION"),
sensitiveFields: configService.get("SENSITIVE_FIELDS", []),
}),
inject: [ConfigService],
});
Usage Examples
Service Logging
import { Injectable } from "@nestjs/common";
import { InjectLogger, LoggerService } from "@globalart/nestjs-logger";
@Injectable()
export class UserService {
constructor(
@InjectLogger()
private readonly logger: LoggerService
) {}
async createUser(userData: CreateUserDto) {
this.logger.log({
message: "Creating new user",
metadata: { email: userData.email },
});
try {
const user = await this.userRepository.save(userData);
this.logger.log({
message: "User created successfully",
metadata: { id: user.id },
});
return user;
} catch (error) {
this.logger.error({
message: "Failed to create user",
trace: error.stack,
metadata: { email: userData.email },
});
throw error;
}
}
}
HTTP Logging
import { Controller, UseInterceptors } from "@nestjs/common";
import { HttpLoggerInterceptor, LogContext } from "@globalart/nestjs-logger";
@Controller("users")
@UseInterceptors(HttpLoggerInterceptor)
@LogContext("UserController")
export class UserController {
// All HTTP requests will be automatically logged
}
Global HTTP Logging
import { Module } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { LoggerModule, HttpLoggerInterceptor } from "@globalart/nestjs-logger";
@Module({
imports: [LoggerModule.forRoot()],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: HttpLoggerInterceptor,
},
],
})
export class AppModule {}
Output Formats
Text Format (Default)
[2024-01-15T10:30:45.123Z] [INFO] [UserService] Creating new user {"email":"user@example.com"}
[2024-01-15T10:30:45.335Z] [INFO] [HttpLogger] GET /users - 200 (12ms)
JSON Format
{
"timestamp": "2024-01-15T10:30:45.123Z",
"level": "info",
"message": "Creating new user",
"context": "UserService",
"metadata": { "email": "user@example.com" }
}
Pino Format
{
"level": 30,
"time": 1642247445123,
"pid": 1234,
"hostname": "app-server",
"req": {
"id": "req-123",
"method": "GET",
"url": "/users",
"remoteAddress": "127.0.0.1"
},
"res": { "statusCode": 200 },
"responseTime": 12,
"msg": "request completed"
}
API Reference
LoggerService Methods
| Method | Description |
|---|---|
log(options: LogOptions) | Information message |
error(options: LogOptions) | Error with stack trace |
warn(options: LogOptions) | Warning message |
debug(options: LogOptions) | Debug information |
verbose(options: LogOptions) | Verbose logging |
LogOptions Interface
interface LogOptions {
message: string;
context?: string;
metadata?: Record<string, unknown>;
trace?: string;
}
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
level | LogLevel | 'info' | Logging level |
timestamp | boolean | true | Show timestamp |
colors | boolean | true | Colored output |
format | LogFormat | 'text' | Output format |
context | string | undefined | Default context |
sensitiveFields | string[] | [...] | Fields to sanitize |
exclude | ExcludeOption[] | [] | HTTP requests to exclude from logging |
logRequests | boolean | false | Log every handled HTTP request |
fieldNaming | FieldNaming | 'camelCase' | Case of the log entry's own field names |
includeHeaders | string[] | undefined | Log only these headers; all of them when unset |
logRequestBody | boolean | true | Include the request body in the log entry |
logResponseBody | boolean | false | Include the response body in the log entry |
maxBodyLength | number | 2048 | Truncate a serialized body longer than this |
Field Naming
Log entries name their own fields in camelCase by default — responseTime, statusCode,
remoteAddress, traceId. Set fieldNaming: "snake_case" when the log pipeline expects snake_case
keys:
LoggerModule.forRoot({
format: "pino",
logRequests: true,
fieldNaming: "snake_case",
});
{ "level": 30, "req": { "remote_address": "10.0.0.1" }, "res": { "status_code": 200 }, "response_time": 56 }
Only the entry's own fields are renamed. Everything the application or the client supplied —
headers, metadata, query, params and the request and response bodies — keeps its keys
verbatim, so a payload field named accountId stays accountId. Applies to the json and pino
formats; text output is unaffected.
HTTP Payload Logging
By default every request header is written to the log entry, which makes lines large enough to be
dropped by log backends that enforce a per-line size limit, and leaks cookie and other
credential-carrying headers into log storage. Pass includeHeaders to keep only the headers you
actually query on, and cap payloads with maxBodyLength:
LoggerModule.forRoot({
format: "pino",
logRequests: true,
includeHeaders: ["user-agent", "x-request-id", "content-type"],
logRequestBody: true,
logResponseBody: true,
maxBodyLength: 2048,
});
Header matching is case-insensitive and applies to both request and response headers.
Bodies are sanitized with sensitiveFields first and truncated afterwards; a truncated body is
replaced by its serialized prefix followed by ...[TRUNCATED <n> chars].
Decorators
@LogContext
Set context for class or method:
@Controller("users")
@LogContext("UserController")
export class UserController {
@Get()
@LogContext("UserController.getAllUsers")
async getAllUsers() {
// Context will be "UserController.getAllUsers"
}
}
@LogMetadata
Add metadata to logs:
@Controller("users")
export class UserController {
@Get()
@LogMetadata({ operation: "getAllUsers", version: "v1" })
async getAllUsers() {
// Metadata will be included in all logs
}
}
@ExcludeLogging
Exclude specific endpoints from logging:
@Controller("health")
export class HealthController {
@Get()
@ExcludeLogging()
async check() {
return { status: "ok" };
}
}
Excluding HTTP Requests
Configure which requests to exclude from logging:
LoggerModule.forRoot({
exclude: [
// Health checks
{ method: RequestMethod.GET, path: "/health" },
{ method: RequestMethod.GET, path: "/metrics" },
// Static assets
{ method: RequestMethod.ALL, path: "/favicon.ico" },
{ method: RequestMethod.ALL, path: "/robots.txt" },
// Wildcard patterns
{ method: RequestMethod.GET, path: "/api/*/health" },
{ method: RequestMethod.ALL, path: "/static/*" },
],
});
Security Features
Automatic Data Sanitization
The logger automatically sanitizes sensitive fields:
password,passtoken,accessToken,refreshTokensecret,key,apiKeyauthorization,authcredential,credentials
Custom Sensitive Fields
LoggerModule.forRoot({
sensitiveFields: ["password", "secret", "customField"],
});
Performance Features
- Minimal allocations in hot paths
- Lazy initialization to reduce startup time
- Optimized context resolution with caching
- Efficient data sanitization with minimal overhead
- Format-specific optimizations for different output types
Customization
Custom Formatter
import { Injectable } from "@nestjs/common";
import { BaseFormatter, LogEntry } from "@globalart/nestjs-logger";
@Injectable()
export class CustomFormatter extends BaseFormatter {
format(entry: LogEntry): string {
return `[${entry.level.toUpperCase()}] ${entry.message}`;
}
}
Custom Writer
import { Injectable } from "@nestjs/common";
import { ILogWriter } from "@globalart/nestjs-logger";
@Injectable()
export class FileWriter implements ILogWriter {
write(formattedLog: string): void {
// Write to file, database, or external service
}
}
Best Practices
- Use Structured Logging - Include relevant metadata in your log entries
- Set Appropriate Log Levels - Use debug/verbose for development, info/warn/error for production
- Exclude Health Checks - Prevent log spam from monitoring endpoints
- Sanitize Sensitive Data - Configure sensitive fields to prevent data leakage
- Use Context - Set meaningful context names for better log organization
- Performance Monitoring - Use HTTP logging to monitor API performance