@urfav/smart-logger
v0.1.0
Published
Configurable Winston logger for NestJS with sensitive-data masking and pluggable request-context enrichment.
Readme
@urfav/smart-logger
A configurable Winston logger for NestJS with built-in sensitive-data masking and pluggable request-context enrichment.
It is the standalone, framework-agnostic evolution of the in-app logger.util helper: every
coupling to a specific app (env schema, CLS store shape, hard-coded filenames, process.env
reads) is now an injected option.
Features
- Sensitive-data masking — redacts configured keys recursively through objects, arrays, and stringified/inline JSON, with circular-reference safety.
- Pluggable context enrichment — inject a
traceId(or any fields) from a request-scoped store via aLogContextProvider. A ready-madenestjs-clsadapter ships in the box. - Optional transports, lazily loaded —
winston-daily-rotate-file,winston-transport-sentry-node, andnest-winston's console formatter are optional peer dependencies; you only install what you use. - First-class NestJS integration —
SmartLoggerModule.forRoot/forRootAsync, plus aSmartLoggerServicethat implements NestJS'sLoggerService. The raw winston instance is also exported forWinstonModule.createLogger({ instance })-style wiring.
Installation
yarn add @urfav/smart-logger winston @nestjs/common reflect-metadata
# install only the optional transports you actually use:
yarn add nest-winston # nestLike console formatting
yarn add winston-daily-rotate-file # rotating file logs
yarn add winston-transport-sentry-node # Sentry error reportingQuick start (NestJS)
import { Module } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import { SmartLoggerModule, ClsContextProvider } from '@urfav/smart-logger';
@Module({
imports: [
SmartLoggerModule.forRootAsync({
isGlobal: true,
inject: [ClsService],
useFactory: (cls: ClsService) => ({
appName: process.env.APP_NAME,
sensitiveKeys: process.env.LOGGER_SENSITIVE_KEYS,
contextProvider: new ClsContextProvider(cls, ['traceId']),
console: process.env.NODE_ENV !== 'prod' ? { nestLike: true } : false,
file: process.env.NODE_ENV === 'prod' ? { filename: 'logs/gateway-%DATE%.log' } : undefined,
sentry: process.env.SENTRY_DSN ? { dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV } : undefined,
}),
}),
],
})
export class AppModule {}Then route NestJS's own logs through it:
import { SmartLoggerService } from '@urfav/smart-logger';
const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.useLogger(app.get(SmartLoggerService));Bootstrap usage (without DI)
The factory works standalone — useful at main.ts before the DI container exists, mirroring the
original WinstonModule.createLogger({ instance }) pattern:
import { WinstonModule } from 'nest-winston';
import { createSmartLogger, ClsContextProvider } from '@urfav/smart-logger';
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger({
instance: createSmartLogger({
appName: process.env.APP_NAME,
sensitiveKeys: process.env.LOGGER_SENSITIVE_KEYS,
contextProvider: new ClsContextProvider(cls),
}),
}),
});Configuration
SmartLoggerOptions:
| Option | Type | Default | Notes |
| ----------------- | ----------------------------------------------- | ----------------------- | --------------------------------------------------------------------- |
| appName | string | — | Used as the nestLike console label. |
| level | string | 'debug' | Minimum level to emit. |
| sensitiveKeys | string \| string[] | [] | Comma-separated string or array of keys to redact. |
| timestampFormat | string | 'YYYY-MM-DD HH:mm:ss' | Winston timestamp format. |
| levelConfig | { levels; colors } | custom 8-level set | Override the level/colour map. |
| contextProvider | LogContextProvider \| () => Record | — | Enriches every record (e.g. traceId). |
| console | ConsoleTransportConfig \| boolean | enabled (nestLike) | false to disable; { nestLike: false } for a plain console. |
| file | FileTransportConfig | disabled | Enables a daily-rotating JSON file transport. |
| sentry | SentryTransportConfig | disabled | Enables the Sentry transport (defaults to level: 'error'). |
| exitOnError | boolean | false | Passed through to winston. |
Transports are driven by which option blocks are present — there is no implicit env switch. The consumer decides what to enable for each environment.
Custom context providers
ClsContextProvider accepts anything with a get(key) method, so it works with a nestjs-cls
ClsService without this package depending on nestjs-cls. For other stores, implement the
interface directly:
import { LogContextProvider } from '@urfav/smart-logger';
class RequestContextProvider implements LogContextProvider {
resolve(): Record<string, unknown> {
return { traceId: currentTrace(), tenantId: currentTenant() };
}
}Development
yarn install
yarn build # tsc -> dist/
yarn test # jest
yarn test:cov # coverageLicense
MIT © Uchenna Nnochirionye
