Skip to content

Pino Provider

PinoLogger is the second logger provider behind the ILogger contract - the throughput option. Where the built-in Winston provider gives colorized console, daily-rotating info/error files, and UDP shipping at ~1.2-1.6us per line, the pino provider emits newline-delimited JSON at ~0.5-0.6us per line (measured). Winston remains the DEFAULT; nothing changes for apps that never register pino.

Registration - one line, order-independent

typescript
// entrypoint (e.g. src/index.ts)
import { LoggerFactory } from '@venizia/ignis-helpers';
import { PinoLogger } from '@venizia/ignis-helpers/pino';

LoggerFactory.use({ provider: PinoLogger });

The two providers register symmetrically - WinstonLogger lives at @venizia/ignis-helpers/winston the same way. Both are sub-path only with optional peers, and exactly ONE provider is ever loaded: registering pino here means winston is never loaded (nor bundled).

From that moment EVERY factory-issued logger runs on pino: BaseHelper.logger in every controller/service/repository/helper, ApplicationLogger.get(...), and - thanks to swap-on-use delegation - even module-level const logger = LoggerFactory.getLogger([...]) constants that were captured at import time, BEFORE this line ran. Import order does not matter; the factory re-points every wrapper it has ever issued when use() is called.

IMPORTANT

Logger.get(...) (the concrete WinstonLogger alias) and defineCustomLogger deliberately do NOT follow the registration - they name winston explicitly. See the name/role table in the Full reference.

Installing the peers

The provider lives at the sub-path @venizia/ignis-helpers/pino only - importing the root barrel never pulls pino into a bundle (test-enforced). Three optional peers, each needed only when its mode is active:

PeerNeeded when
pinoalways (the sub-path values-imports it)
pino-prettyAPP_ENV_LOGGER_FORMAT=text (colorized dev output)
pino-rollAPP_ENV_LOGGER_FOLDER_PATH is set (file rotation)

A missing peer fails with the standard install-hint error BEFORE any worker thread spawns.

Output modes

  • Default (APP_ENV_LOGGER_FORMAT=json, or unset in production practice): NDJSON to stdout - the k8s/docker collector pattern.
  • APP_ENV_LOGGER_FORMAT=text: pretty colorized lines via a pino-pretty worker-thread transport - dev only.
  • APP_ENV_LOGGER_FOLDER_PATH set: writes to a rotating file via pino-roll, honoring the SAME env vars winston uses:
Envpino-roll meaning
APP_ENV_LOGGER_FILE_FREQUENCY'1h' -> hourly (default); '1d'/'24h' -> daily; anything else -> hourly with a warning
APP_ENV_LOGGER_FILE_MAX_SIZEmax size per file (default 100m)
APP_ENV_LOGGER_FILE_MAX_FILESretention -> file count: '5d' -> 120 files (hourly) / 5 (daily); a bare integer -> that count
APP_ENV_LOGGER_FILE_DATE_PATTERNNOT supported (pino-roll has no date pattern)

APP_ENV_LOGGER_LEVEL sets the floor exactly as with winston; emerg is the single custom pino level (above error), and the default debug floor admits every level - identical to the winston provider.

What stays identical, what differs

Identical by construction: the [Scope] message prefix, args formatting through formatLogMessage (deep inspection + secret REDACTION - a token field renders [REDACTED] on pino exactly as on winston), the level vocabulary and floor semantics, the DEBUG gate on debug().

Different on purpose (pino stays pino-native - every parity shim would cost the speed you came for):

AspectWinston providerPino provider
JSON keyslevel (name), message, label, timestamp (ISO)level (NUMBER), msg, name, time (epoch ms), pid, hostname
Text modebuilt-in colorized consolepino-pretty (optional peer, worker thread)
File modedaily-rotate, info/error SPLIT files, date patternpino-roll, ONE file, no date pattern
UDPDgramTransportnone
Console + file simultaneouslyyesno (one destination)
Uncaught-exception fileyes (exceptionHandlers)no

If your operations depend on the left column, stay on winston - it is not deprecated and not going anywhere.

Advanced: injecting a backing instance

setPinoBackingLogger({ instance }) replaces the env-driven singleton with a pino instance you configured yourself (tests use this with an in-memory destination; apps can use it for exotic transports). The previous instance's transport is flushed and closed on replacement. buildPinoOptions() and resolveDestinationPlan() are exported for building compatible options.

See also

Files: