npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pawells/logger-transport-file

v3.0.3

Published

[![npm](https://img.shields.io/npm/v/@pawells/logger-transport-file)](https://www.npmjs.com/package/@pawells/logger-transport-file) [![GitHub Release](https://img.shields.io/github/v/release/PhillipAWells/logger)](https://github.com/PhillipAWells/logger/r

Readme

@pawells/logger-transport-file

npm GitHub Release CI Node License: MIT

File transport for @pawells/logger. Writes structured log entries to disk with automatic size-based rotation. ESM-only with no extra runtime dependencies beyond @pawells/logger.

Features

  • Size-based rotation — rotates the active log file when it exceeds a configurable byte threshold
  • Configurable archive count — retains a bounded number of archived log files and deletes the oldest when the limit is reached
  • JSON output by default — uses JSONLogFormatter out of the box; any LogFormatter can be substituted
  • Automatic directory creation — creates the parent directory (with mode 0o700) if it does not exist
  • Graceful async error handling — initialization failures surface on first write via process.stderr, not during construction
  • Dependency-injectable fs module — pass a mock FsModuleType to the constructor for deterministic unit testing
  • Auto-registers with LogManager — the transport subscribes to log events in its constructor; no separate Register() call is required

Requirements

  • Node.js >= 22.0.0
  • @pawells/logger >= 3.0.0 (peer dependency)

Installation

npm install @pawells/logger @pawells/logger-transport-file
# or
yarn add @pawells/logger @pawells/logger-transport-file

Quick Start

import { Logger, LogManager, ConsoleTransport, LogLevelFilter, LogLevels } from '@pawells/logger';
import { FileTransport } from '@pawells/logger-transport-file';

// Optional: set application-level context
LogManager.Context = 'my-app';

// Console transport — requires explicit Register()
const consoleTransport = new ConsoleTransport({
  filters: [LogLevelFilter(LogLevels.INFO)],
});
consoleTransport.Register();

// File transport — auto-registers in its constructor; do NOT call Register() again
const fileTransport = new FileTransport({
  filePath: '/var/log/my-app/app.log',
  rotation: {
    enabled: true,
    maxFileSize: 10 * 1024 * 1024, // 10 MB
    maxArchives: 5,
  },
});

const logger = new Logger('api');
logger.info('Server started', { port: 3000 });
logger.warn('High memory usage', { memoryPercent: 85 });
logger.error('Request failed', new Error('Connection refused'));

// Graceful shutdown — flush pending writes and close the file handle
process.on('SIGTERM', async () => {
  await fileTransport.close();
  process.exit(0);
});

API Reference

FileTransport

class FileTransport extends LogTransport<IFileTransportOptions>

Writes formatted log entries to a file on disk. Extends LogTransport from @pawells/logger.

Auto-registration: FileTransport calls this.Register('file-transport') inside its constructor. This is the one built-in transport that auto-registers. Every other transport (ConsoleTransport, StreamTransport, MemoryTransport, and all custom transports) requires an explicit Register() call after construction. Do not call Register() on a FileTransport instance — it is already registered.

Constructor

constructor(options: IFileTransportOptions, fsModule?: FsModuleType)
  • options — required configuration; see IFileTransportOptions below
  • fsModule — optional fs/promises-compatible module for dependency injection in tests

Throws TypeError synchronously if filePath is not an absolute path or contains path traversal sequences (../ or ./). All other initialization (directory creation, file open) is deferred to a background promise and surfaces errors on the first write.

Properties

readonly initPromise: Promise<void>

Resolves when async initialization (directory creation and file open) completes. Await this in tests before asserting that writes have occurred. In production code you do not need to await this — errors surface automatically through process.stderr on the first write.

Methods

async close(): Promise<void>

Flushes all pending writes, closes the file handle, and unregisters the transport from LogManager. Safe to call multiple times (idempotent). Does not throw; stream-close errors are written to process.stderr. Call this during application shutdown to ensure all buffered entries are written to disk.

OnPosted(entry: TLogEntry): Promise<void>

Called automatically by LogManager for each log entry. Do not call this directly.


IFileTransportOptions

interface IFileTransportOptions extends ILogTransportOptions {
  filePath: string;
  formatter?: LogFormatter;
  rotation?: IFileRotationOptions;
  filters?: LogEntryPredicate[];
}

| Field | Type | Required | Default | Description | |---|---|---|---|---| | filePath | string | Yes | — | Absolute path to the log file. Relative paths throw TypeError at construction. Parent directory is created automatically. | | formatter | LogFormatter | No | JSONLogFormatter | Formatter used to convert each TLogEntry to a string before writing. | | rotation | IFileRotationOptions | No | See below | File rotation configuration. Omit to use defaults (10 MB threshold, 5 archives, enabled). | | filters | LogEntryPredicate[] | No | undefined (no filtering) | Inherited from ILogTransportOptions. All predicates must return true for an entry to be written. |


IFileRotationOptions

interface IFileRotationOptions {
  enabled?: boolean;
  maxFileSize?: number;
  maxArchives?: number;
}

| Field | Type | Default | Description | |---|---|---|---| | enabled | boolean | true | Whether automatic size-based rotation is active. Set to false to disable rotation entirely. | | maxFileSize | number | 10_485_760 (10 MB) | File size in bytes at which rotation is triggered. Must be at least 1024 bytes. | | maxArchives | number | 5 | Maximum number of archived files to retain. When exceeded, the oldest archive is deleted. Set to 0 to truncate instead of archiving. Maximum value is 100. |


FileRotationError

class FileRotationError extends Error {
  readonly code = 'FILE_ROTATION_ERROR';
  constructor(message: string, cause: unknown)
}

Error class used internally when log rotation fails. FileTransport catches this error and writes it to process.stderr — applications do not receive it via throw. The code property is always 'FILE_ROTATION_ERROR'.


Assertion and Validation Functions

These functions validate options objects and are useful for building wrappers or validating configuration at startup.

AssertFileTransportOptions

function AssertFileTransportOptions(options: unknown): asserts options is IFileTransportOptions

Throws TypeError if options is not a valid IFileTransportOptions object. Checks that filePath is a non-empty absolute string without traversal components, that formatter (if provided) implements LogFormatter, and that rotation (if provided) passes rotation validation.

ValidateFileTransportOptions

function ValidateFileTransportOptions(options: unknown): boolean

Returns true if options passes AssertFileTransportOptions validation, false otherwise. Non-throwing alternative to AssertFileTransportOptions.

AssertFileRotationOptions

function AssertFileRotationOptions(options: unknown): asserts options is IFileRotationOptions

Throws TypeError or RangeError if options is not a valid IFileRotationOptions object. Validates enabled (boolean), maxFileSize (finite number >= 1024), and maxArchives (integer 0–100).

ValidateFileRotationOptions

function ValidateFileRotationOptions(options: unknown): boolean

Returns true if options passes AssertFileRotationOptions validation, false otherwise.


Testing

FileTransport accepts an optional fsModule parameter for dependency injection. Pass a mock that implements FsModuleType to avoid touching the real filesystem in unit tests.

After constructing the transport, await transport.initPromise before making assertions to ensure the async initialization sequence has completed.

Always unregister the transport in afterEach — because FileTransport auto-registers, any test that constructs one must explicitly unregister it on teardown:

import { describe, it, expect, afterEach } from 'vitest';
import { LogManager, Logger } from '@pawells/logger';
import { FileTransport } from '@pawells/logger-transport-file';

describe('FileTransport', () => {
  let transport: FileTransport;

  afterEach(async () => {
    // FileTransport auto-registers, so always unregister on teardown
    await transport.close();
  });

  it('writes a log entry to the mock file system', async () => {
    const written: string[] = [];

    const mockFs = {
      mkdir: async () => undefined,
      open: async () => ({
        write: async (data: string) => { written.push(data); },
        close: async () => undefined,
      }),
      stat: async () => ({ size: 0 }),
      rename: async () => undefined,
      realpath: async (p: string) => p,
    };

    transport = new FileTransport(
      { filePath: '/tmp/test/app.log' },
      mockFs as never,
    );

    // Await initialization before asserting
    await transport.initPromise;

    const logger = new Logger('test');
    logger.info('hello from test');

    // Allow the write promise to settle
    await new Promise<void>((resolve) => setTimeout(resolve, 10));

    expect(written.length).toBeGreaterThan(0);
    expect(written[0]).toContain('hello from test');
  });
});

Development

yarn nx run-many -t build               # Compile TypeScript → ./dist/
yarn nx run-many -t typecheck           # Type check without emitting
yarn nx run-many -t lint                # ESLint
yarn nx run-many -t test                # Run tests with Vitest
yarn nx run-many -t test -- --coverage  # Tests with coverage report

TypeScript Support

All public types are exported from the main entry point:

import {
  FileTransport,
  type FsModuleType,
  type IFileTransportOptions,
  type IFileRotationOptions,
  AssertFileTransportOptions,
  ValidateFileTransportOptions,
  AssertFileRotationOptions,
  ValidateFileRotationOptions,
  FileRotationError,
} from '@pawells/logger-transport-file';

License

MIT — See LICENSE for details.