Pino-based structured logger for NestJS applications with file rotation and pretty printing
📚 Full Documentation | API Reference | Examples
- ✅ Pino-based - Fast, low-overhead structured logging
- ✅ NestJS Integration - Seamless integration with NestJS ecosystem
- ✅ Pretty Printing - Beautiful console output in development
- ✅ File Rotation - Automatic daily log file rotation
- ✅ Configurable - Environment-based configuration
- ✅ TypeScript - Full type safety
- ✅ Context Support - Scoped logging with context
pnpm add @shinijs/logger pino pino-pretty pino-rollThis package requires the following peer dependencies to be installed in your project:
| Package | Version | Required |
|---|---|---|
@nestjs/common |
^11.0.0 |
Yes |
@nestjs/config |
^4.0.0 |
Yes |
pino |
^10.0.0 |
Yes |
pino-pretty |
^13.0.0 |
Yes |
pino-roll |
^4.0.0 |
Yes |
reflect-metadata |
^0.2.0 |
Yes |
Install all peer dependencies:
pnpm add @nestjs/common@^11.0.0 @nestjs/config@^4.0.0 pino@^10.0.0 pino-pretty@^13.0.0 pino-roll@^4.0.0 reflect-metadata@^0.2.0Note: If you're already using NestJS, you likely have @nestjs/common, @nestjs/config, and reflect-metadata installed. You only need to ensure pino, pino-pretty, and pino-roll are present.
import { LoggerModule } from '@shinijs/logger';
@Module({
imports: [LoggerModule],
// ...
})
export class AppModule {}import { CustomLogger } from '@shinijs/logger';
@Injectable()
export class YourService {
constructor(private readonly logger: CustomLogger) {
this.logger.setContext('YourService');
}
doSomething() {
this.logger.log('Doing something');
this.logger.error('Something went wrong', { error: 'details' });
}
}Set environment variables:
# Log level: trace, debug, info, warn, error, fatal
LOG_LEVEL=info
# Pretty print in development
LOG_PRETTY_PRINT=true
# File logging
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logsMain logger service implementing NestJS LoggerService.
Methods:
// Set logging context
setContext(context: string): void
// Log levels
log(message: string, metadata?: object): void
error(message: string, metadata?: object): void
warn(message: string, metadata?: object): void
debug(message: string, metadata?: object): void
verbose(message: string, metadata?: object): voidFactory for creating context-bound loggers. The ContextBoundLogger implements NestJS LoggerService for full compatibility.
import { LoggerFactory } from '@shinijs/logger';
@Injectable()
export class MyService {
private readonly logger = LoggerFactory.createLogger('MyService');
doWork() {
this.logger.log('Working...');
// ContextBoundLogger implements LoggerService, so it works with NestJS's standard logger interface
}
}import { CustomLogger } from '@shinijs/logger';
@Injectable()
export class UserService {
constructor(private readonly logger: CustomLogger) {
this.logger.setContext('UserService');
}
async createUser(data: CreateUserDto) {
this.logger.log('Creating user', { email: data.email });
try {
const user = await this.userRepository.create(data);
this.logger.log('User created successfully', { userId: user.id });
return user;
} catch (error) {
this.logger.error('Failed to create user', { error, email: data.email });
throw error;
}
}
}import { LoggerFactory } from '@shinijs/logger';
export class SomeClass {
private readonly logger = LoggerFactory.createLogger('SomeClass');
process() {
this.logger.debug('Processing started');
// ... your logic
this.logger.debug('Processing completed');
}
}this.logger.log('Request processed', {
method: 'POST',
path: '/api/users',
statusCode: 201,
duration: 45,
userId: '123',
});Use the logger with @shinijs/rate-limit for consistent logging:
import { Module, Global } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { LoggerModule, LoggerFactory } from '@shinijs/logger';
import { RateLimitModule } from '@shinijs/rate-limit';
// Token for RateLimit logger provider
export const RATE_LIMIT_LOGGER_TOKEN = Symbol('RATE_LIMIT_LOGGER');
// Create a global module to provide the logger token
@Global()
@Module({
providers: [
{
provide: RATE_LIMIT_LOGGER_TOKEN,
useFactory: (loggerFactory: LoggerFactory) => {
return loggerFactory.createLogger('RateLimit');
},
inject: [LoggerFactory],
},
],
exports: [RATE_LIMIT_LOGGER_TOKEN],
})
class RateLimitLoggerModule {}
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
LoggerModule,
RateLimitLoggerModule,
RateLimitModule.forRoot({
loggerToken: RATE_LIMIT_LOGGER_TOKEN,
}),
],
})
export class AppModule {}| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Minimum log level | info |
LOG_PRETTY_PRINT |
Enable pretty console output | false |
LOG_FILE_ENABLED |
Enable file logging | false |
LOG_FILE_PATH |
Directory for log files | ./logs |
LOG_FILE_ROTATION_ENABLED |
Enable automatic log file rotation | true* |
LOG_FILE_ROTATION_FREQUENCY |
Rotation frequency: daily, hourly, custom |
daily |
LOG_FILE_ROTATION_PATTERN |
Date pattern for rotated filenames | YYYY-MM-DD |
LOG_FILE_MAX_FILES |
Number of log files to retain | 7 |
LOG_FILE_SIZE_LIMIT |
Maximum file size before rotation | 10M |
* Rotation is enabled by default when LOG_FILE_ENABLED=true. Set to false to use a single log file without rotation.
From lowest to highest priority:
trace- Very detailed debuggingdebug- Debugging informationinfo- Informational messages (default)warn- Warning messageserror- Error messagesfatal- Fatal errors
When file logging is enabled, logs are written to files in the specified directory with automatic rotation and cleanup.
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logsKey Features:
- File logging works simultaneously with pretty printing - you can have both console output and file logging in development
- Directory is created automatically if it doesn't exist
- Automatic log file rotation enabled by default
- Old log files are automatically cleaned up based on retention policy
Log rotation is enabled by default when file logging is enabled. This prevents log files from growing indefinitely and helps manage disk space.
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logs
LOG_FILE_ROTATION_FREQUENCY=daily
LOG_FILE_MAX_FILES=7File naming pattern: app-YYYY-MM-DD.log
Example files:
logs/
├── app-2024-01-15.log (today)
├── app-2024-01-14.log
├── app-2024-01-13.log
├── app-2024-01-12.log
├── app-2024-01-11.log
├── app-2024-01-10.log
└── app-2024-01-09.log (oldest, will be deleted when new day starts)
Automatic cleanup: When the 8th day arrives, the oldest file (app-2024-01-09.log) is automatically deleted, keeping only the most recent 7 days of logs.
For high-traffic applications or more granular log management:
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logs
LOG_FILE_ROTATION_FREQUENCY=hourly
LOG_FILE_ROTATION_PATTERN=YYYY-MM-DD-HH
LOG_FILE_MAX_FILES=24File naming pattern: app-YYYY-MM-DD-HH.log
Example files:
logs/
├── app-2024-01-15-14.log (current hour)
├── app-2024-01-15-13.log
├── app-2024-01-15-12.log
└── ... (keeps last 24 hours)
Rotate logs when they reach a specific size:
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logs
LOG_FILE_SIZE_LIMIT=50M # Rotate when file reaches 50MBSupported size units: K (kilobytes), M (megabytes), G (gigabytes)
To use a single log file without rotation (legacy behavior):
LOG_FILE_ENABLED=true
LOG_FILE_PATH=./logs
LOG_FILE_ROTATION_ENABLED=falseFile naming: app.log (single file, grows indefinitely)
Use case: Suitable when you have external log management tools or limited logging needs.
| Setting | Options | Description |
|---|---|---|
| Frequency | daily, hourly, custom |
How often to create new log files |
| Pattern | Date format string | Filename pattern (e.g., YYYY-MM-DD, YYYY-MM-DD-HH) |
| Max Files | Number (e.g., 7, 24, 30) |
Number of log files to keep before deleting oldest |
| Size Limit | Size string (e.g., 10M, 50M, 1G) |
Maximum file size before rotation |
Production configuration with daily rotation and 30-day retention:
# Basic logging
LOG_LEVEL=info
LOG_PRETTY_PRINT=false
# File logging with rotation
LOG_FILE_ENABLED=true
LOG_FILE_PATH=/var/log/myapp
# Rotation settings
LOG_FILE_ROTATION_ENABLED=true
LOG_FILE_ROTATION_FREQUENCY=daily
LOG_FILE_ROTATION_PATTERN=YYYY-MM-DD
LOG_FILE_MAX_FILES=30
LOG_FILE_SIZE_LIMIT=100MThis configuration will:
- Create daily log files:
app-2024-01-15.log,app-2024-01-14.log, etc. - Keep the last 30 days of logs
- Rotate early if a file exceeds 100MB
- Automatically delete logs older than 30 days
Development (with LOG_PRETTY_PRINT=true):
14:23:45 INFO [UserService] Creating user
14:23:45 INFO [UserService] User created successfully
Production (JSON output):
{"level":30,"time":1699999999,"context":"UserService","msg":"Creating user","email":"user@example.com"}
{"level":30,"time":1700000000,"context":"UserService","msg":"User created successfully","userId":"123"}# Unit tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:cov📚 Full Documentation is available with:
- Complete API reference
- Configuration guide
- Usage examples
- Best practices
- Advanced patterns
Contributions are welcome! Please read our Contributing Guide and Code of Conduct before submitting pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and linting (
pnpm test && pnpm lint:check) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
For detailed guidelines, see CONTRIBUTING.md.
MIT © Shironex