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

logkitten

v1.3.3

Published

Stream Android and iOS logs without Android Studio or Console.app, with programmatic Node.js API for log analysis.

Readme

logkitten

Version PRs Welcome MIT License

Stream Android and iOS logs without Android Studio or Console.app, with programmatic Node.js API for log analysis.

This is a Wix Incubator fork of the original logkitty project.

Installation

npm install logkitten
# or
yarn add logkitten

Usage

import { logkitten, Level } from 'logkitten';

// Basic usage - get all Android logs
const emitter = logkitten({
  platform: 'android',
});

emitter.on('entry', (entry) => {
  // Process the structured log entry
  console.log({
    ts: new Date(entry.ts),
    level: entry.level,
    pid: entry.pid,
    tid: entry.tid,
    message: entry.msg,
  });
});

emitter.on('error', (error: Error) => {
  console.error('Logging error:', error.message);
});

// When you're done listening to logs, close the stream programmatically
await emitter.close(); // or emitter.close(callback)

API Reference

Main Function

logkitten(options)

The main function that accepts platform-specific options:

// Android
const androidEmitter = logkitten({
  platform: 'android',
  // Optional: specify custom adb path
  adbPath: '/custom/path/to/adb',
  // Optional: target specific device
  deviceId: 'emulator-5554',
  // Optional: filter entries
  filter: (entry) => entry.tag === 'MyApp'
});

// iOS
const iosEmitter = logkitten({
  platform: 'ios',
  // Optional: target specific simulator (defaults to 'booted')
  deviceId: 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890',
  // Optional: filter entries
  filter: (entry) => entry.processImagePath.includes('/MyApp')
});

Entry Structure

Each log entry is a structured object with platform-specific extensions:

interface Entry {
  msg: string;           // Message content
  ts: number;           // Timestamp in milliseconds
  pid: number;          // Process ID
  tid: number;          // Thread ID
  level: number;        // Log level/priority (Level enum)
}

interface AndroidEntry extends Entry {
  tag: string;          // Log tag
  uid: string;          // User ID
}

interface IosEntry extends Entry {
  category: string;           // iOS category
  formatString: string;       // Format string used for the log message
  processImagePath: string;   // Path to the process executable
  subsystem: string;          // iOS subsystem
  userID: number;            // User ID
}

LogkittenEmitter

The emitter returned by all functions extends Node's EventEmitter and provides:

  • Events

    • entry (arguments: entry: AndroidEntry | IosEntry) – Emitted when a new log entry passes filters.
    • error (arguments: error: Error) – Emitted when parsing fails or the underlying process encounters errors.
    • close – Emitted once the logging process is terminated via .close().
  • Methods

    • close(cb?) – Gracefully stops the underlying logging process and returns a Promise that resolves when cleanup is complete.

Levels

import { Level } from 'logkitten';

// Available priorities (from lowest to highest):
Level.TRACE    // 10 - Trace level (Android verbose)
Level.DEBUG    // 20 - Debug level
Level.INFO     // 30 - Info level
Level.WARN     // 40 - Warning level
Level.ERROR    // 50 - Error level
Level.FATAL    // 60 - Fatal level

Filtering

You can provide a custom filter function to control which log entries are emitted:

const emitter = logkitten({
  platform: 'android',
  filter: (entry) => {
    // Only include entries with specific tags
    return entry.tag === 'MyApp' || entry.tag === 'ReactNative';
  }
});

Filter Examples

Filter by tag (Android):

(entry) => entry.tag === 'MyApp'

Filter by process (both platforms):

(entry) => entry.pid === myAppPid

Filter by message content:

filter: (entry) => /error|warning/i.test(entry.msg)

Filter by subsystem (iOS):

filter: (entry) => entry.subsystem === 'com.mycompany.myapp'

Multi-criteria filtering:

(entry) => {
  const isMyApp = entry.tag === 'MyApp' || entry.processImagePath?.includes('MyApp');
  const isError = entry.level >= Level.ERROR;
  const hasKeyword = entry.msg.includes('network');
  return isMyApp && (isError || hasKeyword);
}

Examples

Log Analysis and Error Tracking

const emitter = logkitten({
  platform: 'android',
});

const errorCount = new Map();

emitter.on('entry', (entry) => {
  if (entry.level >= Level.ERROR) {
    const count = errorCount.get(entry.tag) || 0;
    errorCount.set(entry.tag, count + 1);
  }
});

// Log error summary every 10 seconds
setInterval(() => {
  console.log('Error counts by tag:', Object.fromEntries(errorCount));
}, 10000);

Structured Log Storage

const emitter = logkitten({
  platform: 'ios',
  filter: (entry) => entry.processImagePath.includes('/MyApp'),
});

const logs = [];

emitter.on('entry', (entry) => {
  // Store structured log data
  logs.push({
    timestamp: entry.ts,
    level: entry.level,
    source: entry.subsystem,
    content: entry.msg,
  });

  // Keep only last 1000 entries
  if (logs.length > 1000) {
    logs.shift();
  }
});

Credits

This project is a fork and continuation of the original logkitty by Paweł Trysła (@zamotany), maintained by Wix Incubator.

The original project has been inactive since 2020, and this fork continues development with modern dependencies and bug fixes. While we maintain this fork primarily for our own use, we welcome community contributions and will consider issues and feature requests as time permits.