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

@devms/livetail

v0.1.0

Published

Live Tail NestJS module for the Monitoring platform. Real-time WebSocket log streaming and raw stdout/stderr console capture over Socket.IO.

Readme

@devms/livetail

Live Tail module for the Monitoring platform. A NestJS WebSocket gateway that broadcasts structured logs to connected clients via Socket.IO — like tail -f for your application logs — plus optional console capture that streams the raw stdout/stderr of your process, like docker logs -f.

Install

pnpm add @devms/livetail @nestjs/websockets @nestjs/platform-socket.io

Quick Start

// app.module.ts
import { LiveTailModule } from '@devms/livetail';

@Module({
  imports: [
    LiveTailModule.register({
      captureConsole: {
        token: process.env.LIVETAIL_CONSOLE_TOKEN, // recommended in prod
      },
    }),
  ],
})
export class AppModule {}

The module is @Global() — register it once and inject LiveTailService or ConsoleCaptureService anywhere.

With ConfigService (async)

LiveTailModule.registerAsync({
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    cors: config.get('LIVETAIL_CORS_ORIGIN', '*'),
    maxClients: parseInt(config.get('LIVETAIL_MAX_CLIENTS', '0')),
    disabled: config.get('LIVETAIL_DISABLED') === 'true',
    captureConsole: {
      bufferSize: parseInt(config.get('LIVETAIL_CONSOLE_BUFFER', '2000')),
      token: config.get('LIVETAIL_CONSOLE_TOKEN'),
    },
  }),
});

Configuration

| Option | Type | Default | Description | | ---------------- | ---------------------------- | -------------- | --------------------------------------------------------- | | namespace | string | '/live-tail' | WebSocket namespace path | | cors | string \| string[] \| bool | '*' | CORS origin for WebSocket connections | | maxClients | number | 0 | Max simultaneous clients (0 = unlimited) | | pingInterval | number | 25000 | Socket.IO ping interval in ms | | pingTimeout | number | 20000 | Socket.IO ping timeout in ms | | disabled | boolean | false | Disable the gateway (service still injectable but no-ops) | | captureConsole | boolean \| object | false | Stream raw stdout/stderr — see below |

Broadcast logs from your service

import { LiveTailService, LiveTailEnvContext } from '@devms/livetail';

@Injectable()
export class AppLogService {
  constructor(private readonly liveTail: LiveTailService) {}

  async ingestLogs(envId: string, logs: LogDto[], ctx: LiveTailEnvContext) {
    await this.db.appLog.createMany({ data: logs });

    // Broadcast to live tail clients, enriched with org/app/env names
    this.liveTail.broadcastMany(
      logs.map((log) => ({
        environmentId: envId,
        level: log.level,
        category: log.category,
        action: log.action,
        message: log.message,
        createdAt: new Date().toISOString(),
      })),
      ctx, // optional LiveTailEnvContext — adds orgName, appName, envName
    );
  }
}

WebSocket protocol

Clients connect to ws://<host>:<port>/live-tail (Socket.IO).

Client → server

| Event | Payload | Description | | ------------------ | ---------------- | ------------------------------------------------- | | subscribe | LiveTailFilter | Start receiving logs matching the given filter | | updateFilter | LiveTailFilter | Update filters without reconnecting | | pause / resume | — | Pause / resume the stream while staying connected |

Server → client

| Event | Payload | Description | | --------------- | ----------------------------------------- | ------------------------------------------- | | connected | { clientId, message, connectedClients } | Connection confirmed | | subscribed | { filter } | Subscription confirmed | | filterUpdated | { filter } | Filter update confirmed | | log | LiveTailLogEvent | A log entry matching your filter | | error | { message } | Connection error (e.g. max clients reached) |

All LiveTailFilter fields are optional: environmentId, appId, orgId, level (single or array), category (exact), action (contains), userId, search (full-text in message/action/category), tags.

import { io } from 'socket.io-client';

const socket = io('http://localhost:5002/live-tail');
socket.on('connected', () => {
  socket.emit('subscribe', {
    environmentId: 'env-abc',
    level: ['error', 'fatal'],
  });
});
socket.on('log', (log) =>
  console.log(`[${log.level}] ${log.category}/${log.action}`),
);

Console Capture (raw stdout/stderr streaming)

Stream the raw terminal output of your application — everything the process writes to stdout/stderr (NestJS Logger, console.log, crash stack traces, third-party output, ANSI colors included) — like docker logs -f, but over the same /live-tail socket. Nothing is persisted; lines are held in an in-memory ring buffer and replayed to clients on subscribe.

LiveTailModule.register({
  captureConsole: {
    bufferSize: 2000, // lines kept in memory & replayed on subscribe
    maxLineLength: 8192, // longer lines are truncated
    batchInterval: 150, // ms between batched pushes to clients
    token: process.env.LIVETAIL_CONSOLE_TOKEN, // require a shared secret
    stripAnsi: false, // keep colors (dashboard renders them)
  },
});

Pass captureConsole: true for the defaults. No logger changes are needed — the module tees process.stdout / process.stderr; your terminal/PM2/Docker output is untouched.

Client protocol

const socket = io('http://localhost:5002/live-tail');

// Subscribe (replays the buffer, then streams live)
socket.emit('subscribe-console', { token: '...', tail: 1000 });

socket.on('console-subscribed', ({ pid, bufferSize, historyCount }) => {});
socket.on('console-history', ({ lines, done }) => {}); // chunked replay
socket.on('console-lines', ({ lines, dropped }) => {}); // live batches
socket.on('console-error', ({ code, message }) => {}); // disabled / bad token

socket.emit('unsubscribe-console');

Each line: { seq: number, stream: 'stdout' | 'stderr', line: string, ts: string }.

Performance & safety

  • Zero subscribers → near-zero cost. Lines only go to the ring buffer; nothing is broadcast.
  • Batched delivery. Live lines are flushed every batchInterval ms as a single frame, and the pending queue is hard-capped (oldest dropped, with a dropped count reported) so a slow consumer can never grow memory.
  • Crash-safe tee. The original write always executes first; any error in capture is swallowed and can never break the app's own output.
  • Security. Raw console output can contain secrets (connection strings, error dumps). Set token in production and restrict cors.

Using with @devms/applog-client

Live Tail was previously bundled inside @devms/applog-client and auto-registered by AppLogModule via the livetail config key. It is now a standalone package — register it alongside the applog module:

import { AppLogModule } from '@devms/applog-client';
import { LiveTailModule } from '@devms/livetail';

@Module({
  imports: [
    AppLogModule.register({
      /* ...credentials */
    }),
    LiveTailModule.register({
      captureConsole: { token: process.env.LIVETAIL_CONSOLE_TOKEN },
    }),
  ],
})
export class AppModule {}

License

MIT