SilentLogger
The SilentLogger suppresses all log output completely. It’s designed for testing environments and scenarios where logging is not desired or would interfere with output.
Overview
SilentLogger implements the ILogger interface but discards all log messages. This is particularly useful in unit tests where you want to test the migration logic without cluttering test output with log messages.
Features
- ✅ Zero configuration required
- ✅ Suppresses all log output
- ✅ Implements full ILogger interface
- ✅ Zero performance overhead
- ✅ Perfect for testing
Installation
SilentLogger is included in the core MSR package:
import { SilentLogger } from 'msr-core';
Basic Usage
In Unit Tests
import { MigrationService, SilentLogger } from 'msr-core';
import { expect } from 'chai';
describe('MigrationService', () => {
it('should execute migrations successfully', async () => {
// Use SilentLogger to keep test output clean
const logger = new SilentLogger();
const service = new MigrationService(logger);
const result = await service.executeMigrations(config);
expect(result.success).to.be.true;
// No log output during test execution
});
});
In Integration Tests
import { MigrationScriptExecutor, SilentLogger } from 'msr-core';
describe('Migration execution', () => {
let executor: MigrationScriptExecutor;
beforeEach(() => {
// Silent logger prevents test output pollution
executor = new MigrationScriptExecutor({ handler,
logger: new SilentLogger()
});
});
it('should handle migration errors', async () => {
await expect(
executor.executeMigration(invalidScript, context)
).to.be.rejected;
});
});
Use Cases
1. Unit Testing
Keep test output focused on test results, not migration logs:
import { describe, it, expect } from '@jest/globals';
import { BackupService, SilentLogger } from 'msr-core';
describe('BackupService', () => {
it('should create backup successfully', async () => {
const logger = new SilentLogger();
const service = new BackupService(logger);
const backupPath = await service.createBackup(config, version);
expect(backupPath).toBeDefined();
expect(fs.existsSync(backupPath)).toBe(true);
});
});
2. CI/CD Pipelines
Reduce noise in CI logs when you only care about test pass/fail:
// test/setup.ts
import { SilentLogger } from 'msr-core';
// Global test configuration
export const testLogger = new SilentLogger();
// test/migrations.spec.ts
import { testLogger } from './setup';
import { MigrationService } from 'msr-core';
describe('Migration tests', () => {
const service = new MigrationService(testLogger);
// Tests run without migration log output
});
3. Programmatic Usage
When embedding MSR in applications where logging is handled elsewhere:
import { MigrationService, SilentLogger } from 'msr-core';
import { myCustomLogger } from './logger';
async function runMigrations() {
// Suppress MSR's internal logging
const silentLogger = new SilentLogger();
const service = new MigrationService(silentLogger);
try {
myCustomLogger.info('Starting migrations');
await service.executeMigrations(config);
myCustomLogger.info('Migrations completed');
} catch (error) {
myCustomLogger.error('Migration failed', error);
throw error;
}
}
4. Silent Batch Operations
When processing many migrations where individual log messages are overwhelming:
import { MigrationScriptExecutor, SilentLogger } from 'msr-core';
async function batchExecute(scripts: MigrationScript[]) {
const executor = new MigrationScriptExecutor({ handler,
logger: new SilentLogger()
});
console.log(`Processing ${scripts.length} migrations...`);
for (const script of scripts) {
await executor.executeMigration(script, context);
// No log spam for each migration
}
console.log('All migrations completed');
}
Complete Example: Test Suite
Here’s a complete test suite using SilentLogger:
import { describe, it, expect, beforeEach } from '@jest/globals';
import {
MigrationService,
MigrationScriptExecutor,
BackupService,
SilentLogger,
MigrationConfig
} from 'msr-core';
describe('Migration System', () => {
let logger: SilentLogger;
let config: MigrationConfig;
beforeEach(() => {
logger = new SilentLogger();
config = {
migrationsPath: './test/fixtures/migrations',
// ... other config
};
});
describe('MigrationService', () => {
it('should read migration scripts', async () => {
const service = new MigrationService(logger);
const scripts = await service.readMigrationScripts(config);
expect(scripts).toHaveLength(3);
});
it('should execute all pending migrations', async () => {
const service = new MigrationService(logger);
const result = await service.executeMigrations(config);
expect(result.executed).toBe(3);
expect(result.failed).toBe(0);
});
});
describe('BackupService', () => {
it('should create backup before migration', async () => {
const service = new BackupService(logger);
const backupPath = await service.createBackup(config, 'v1.0.0');
expect(fs.existsSync(backupPath)).toBe(true);
});
});
// All tests run silently - no migration logs in test output
});
Comparison with ConsoleLogger
// With ConsoleLogger (verbose test output)
const consoleLogger = new ConsoleLogger();
const service = new MigrationService(consoleLogger);
await service.executeMigrations(config);
// Test output:
// Reading migration scripts from ./migrations
// Found 3 migration scripts
// Executing V202311020036_add_users_table.ts
// Migration V202311020036_add_users_table.ts completed
// ...
// With SilentLogger (clean test output)
const silentLogger = new SilentLogger();
const service = new MigrationService(silentLogger);
await service.executeMigrations(config);
// Test output:
// (nothing - just test results)
Integration with Test Frameworks
Mocha/Chai
import { expect } from 'chai';
import { MigrationService, SilentLogger } from 'msr-core';
describe('Migrations', () => {
it('should execute successfully', async () => {
const service = new MigrationService(new SilentLogger());
const result = await service.executeMigrations(config);
expect(result.success).to.be.true;
});
});
Jest
import { MigrationService, SilentLogger } from 'msr-core';
describe('Migrations', () => {
it('should execute successfully', async () => {
const service = new MigrationService(new SilentLogger());
const result = await service.executeMigrations(config);
expect(result.success).toBe(true);
});
});
Vitest
import { describe, it, expect } from 'vitest';
import { MigrationService, SilentLogger } from 'msr-core';
describe('Migrations', () => {
it('should execute successfully', async () => {
const service = new MigrationService(new SilentLogger());
const result = await service.executeMigrations(config);
expect(result.success).toBe(true);
});
});
Best Practices
1. Use in All Tests
// Good: Consistent silent logging in tests
const logger = new SilentLogger();
// Avoid: Mix of loggers in tests
const logger = Math.random() > 0.5 ? new ConsoleLogger() : new SilentLogger();
2. Create Once, Reuse
// Good: Single instance for test suite
const testLogger = new SilentLogger();
beforeEach(() => {
service = new MigrationService(testLogger);
});
// Avoid: Creating new instance each time
beforeEach(() => {
service = new MigrationService(new SilentLogger());
});
3. Document When Used
// Good: Clear why silent logger is used
describe('MigrationService', () => {
// Using SilentLogger to keep test output clean and focused
const logger = new SilentLogger();
// tests...
});
API Reference
Constructor
constructor()
Creates a new SilentLogger instance. No configuration required.
Methods
All methods accept a message string and optional additional arguments, but all output is suppressed:
info(message: string, ...args: unknown[]): void
Suppresses informational messages. No output produced.
warn(message: string, ...args: unknown[]): void
Suppresses warning messages. No output produced.
error(message: string, ...args: unknown[]): void
Suppresses error messages. No output produced.
debug(message: string, ...args: unknown[]): void
Suppresses debug messages. No output produced.
log(message: string, ...args: unknown[]): void
Suppresses general messages. No output produced.
Performance
SilentLogger has zero performance overhead - method calls are no-ops:
export class SilentLogger implements ILogger {
info(message: string, ...args: unknown[]): void {}
warn(message: string, ...args: unknown[]): void {}
error(message: string, ...args: unknown[]): void {}
debug(message: string, ...args: unknown[]): void {}
log(message: string, ...args: unknown[]): void {}
}
This makes it ideal for performance-critical code paths where logging is disabled.