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

@quan-hyaku/log-my-app

v0.1.5

Published

Browser logging library that intercepts console methods and persists logs to IndexedDB with localStorage fallback

Readme

@quan-hyaku/log-my-app

Browser logging library that intercepts console.log, console.warn, console.error, console.info, and console.debug, persisting all output to IndexedDB (with automatic localStorage fallback). Retrieve, filter, or download your logs at any time.

Includes a custom Logger API with tag-based grouping for structured logging alongside automatic console interception.

Zero external dependencies.

Installation

npm install @quan-hyaku/log-my-app

Usage

Console interception

import { initLogger, getLogs, getLogsByLevel, clearLogs, downloadLogs, destroyLogger } from '@quan-hyaku/log-my-app';

// Start intercepting console methods
await initLogger();

// Use console as normal — all output is automatically persisted
console.log('App started');
console.warn('Disk space low');
console.error('Failed to fetch', { status: 500 });

// Retrieve all persisted logs
const logs = await getLogs();

// Filter by level
const errors = await getLogsByLevel('error');

// Download logs as a file
await downloadLogs('json'); // or 'txt'

// Clear all stored logs
await clearLogs();

// Stop intercepting and clean up
destroyLogger();

Custom Logger with tags

import { initLogger, Logger, getLogsByTag, destroyLogger } from '@quan-hyaku/log-my-app';

await initLogger();

// Log without a tag (writes directly to storage, no console output)
Logger.info('App started');
Logger.error('Unexpected failure', { code: 500 });

// Log with a tag for grouping/filtering
Logger.tag('auth').info('User logged in', { userId: 42 });
Logger.tag('auth').warn('Session expiring soon');
Logger.tag('network').error('Request timeout', { url: '/api/data' });

// Retrieve logs by tag
const authLogs = await getLogsByTag('auth');
const networkLogs = await getLogsByTag('network');

destroyLogger();

The Logger writes directly to storage without producing console output. Console interception and the Logger API work independently -- use both together or either one alone.

Uncaught error capture

import { initLogger, getLogsByTag, destroyLogger } from '@quan-hyaku/log-my-app';

await initLogger({ captureUncaughtErrors: true });

// Uncaught errors and unhandled promise rejections are now
// automatically captured and stored as log entries.
//
// - Uncaught errors are tagged 'uncaught'
// - Unhandled rejections are tagged 'unhandled-rejection'
// - Both are stored at the 'error' level

// Retrieve captured errors by tag
const uncaught = await getLogsByTag('uncaught');
const rejections = await getLogsByTag('unhandled-rejection');

destroyLogger();

Each captured entry includes the serialized Error (name, message, stack) along with source location metadata (filename, line number, column number) when available.

Configuration

Pass options to initLogger():

await initLogger({
  maxLogCount: 10000,            // Maximum log entries to keep (default: 5000)
  storageKey: 'my-app-logs',     // Storage key name (default: 'log-my-app')
  captureUncaughtErrors: true,   // Capture uncaught errors and unhandled rejections (default: false)
  maxDepth: 3,                   // Max object nesting depth for serialization (default: 2)
  captureStackTraces: false      // Include stack traces in error serialization (default: true)
});

Storage behavior

  • IndexedDB is used by default when available
  • localStorage is used automatically as a fallback (e.g., Firefox Private Browsing, restricted environments)
  • localStorage mode caps entries at 1000 regardless of maxLogCount to stay within browser storage limits
  • Log rotation happens periodically -- oldest entries are trimmed when the cap is exceeded

Same-origin storage access: Both IndexedDB and localStorage are scoped to the page origin. Any script running on the same origin can read the stored logs. Avoid logging sensitive data (tokens, passwords, PII) if your page loads third-party scripts.

API Reference

initLogger(config?: LoggerConfig): Promise<void>

Initializes the logger, sets up storage, and patches console methods. Throws if called while already initialized.

destroyLogger(): void

Restores original console methods and closes the storage connection.

getLogs(): Promise<LogEntry[]>

Returns all stored log entries, oldest first.

getLogsByLevel(level: LogLevel): Promise<LogEntry[]>

Returns log entries filtered by level. Valid levels: 'log', 'warn', 'error', 'info', 'debug'.

getLogsByTag(tag: string): Promise<LogEntry[]>

Returns log entries that match the given tag.

clearLogs(): Promise<void>

Deletes all stored log entries.

downloadLogs(format?: 'json' | 'txt'): Promise<void>

Triggers a file download of all stored logs.

  • 'json' (default) — prettified JSON array of LogEntry objects
  • 'txt' — human-readable format: [timestamp] [LEVEL] message args

Logger

A singleton object for structured logging that writes directly to storage (no console output). Must be used after initLogger() has been called.

  • Logger.log(message, ...args) — log at 'log' level
  • Logger.info(message, ...args) — log at 'info' level
  • Logger.warn(message, ...args) — log at 'warn' level
  • Logger.error(message, ...args) — log at 'error' level
  • Logger.debug(message, ...args) — log at 'debug' level
  • Logger.tag(name) — returns a TaggedLogger that attaches the given tag to every entry

TaggedLogger

Returned by Logger.tag(name). Has the same five methods (log, info, warn, error, debug) but every entry is tagged with the name passed to .tag().

const auth = Logger.tag('auth');
auth.info('login succeeded');  // entry.tag === 'auth'
auth.error('token expired');   // entry.tag === 'auth'

Types

type LogLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';

interface LogEntry {
  timestamp: string;  // ISO 8601 UTC
  level: LogLevel;
  message: string;    // First console argument, stringified
  args: string[];     // Remaining arguments, each stringified
  tag?: string;       // Optional tag for grouping (set via Logger.tag())
}

interface LoggerConfig {
  maxLogCount?: number;          // default: 5000
  storageKey?: string;           // default: 'log-my-app'
  captureUncaughtErrors?: boolean; // default: false
  maxDepth?: number;             // default: 2
  captureStackTraces?: boolean;  // default: true
}

License

MIT