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

@devraj-labs/logger

v1.0.1

Published

Production-grade, platform-independent logging library for JavaScript/TypeScript (React Native + Web)

Readme

Logger

Production-grade, platform-independent logging for JavaScript & TypeScript. Works in React Native, Node.js, and the browser — zero runtime dependencies.

npm version license types


Why

Most loggers are either too simple (no structure, no transports) or too heavy (Node-only, tons of deps). This one is neither.

  • Only logs. Never stores. Never sends.
  • Your app decides where logs go via pluggable transports.
  • Same API across React Native, Node, and the browser.
  • Zero runtime dependencies.

Features

| | | |---|---| | 5 log levels | debug · info · warn · error · fatal | | Structured logging | Attach typed metadata objects to every log entry | | Child loggers | Scope a logger to a module or request — context stamps every line | | Pluggable transports | Console, batch, or write your own in ~10 lines | | Smart defaults | Pretty output in dev, JSON in production — auto-detected | | Singleton | One instance across your entire app, no prop-drilling | | BatchTransport | Buffer logs and flush in one batch instead of per-line HTTP calls | | Per-transport level filtering | Console shows debug; Sentry only gets error and above | | Graceful shutdown | flush() ensures buffered logs are sent before process exit | | Zero dependencies | Nothing in node_modules that you didn't ask for |


Install

npm install @devraj-labs/logger
# or
yarn add @devraj-labs/logger

Quick Start

import { createLogger } from "@devraj-labs/logger";

// Call once at your app entry point
const logger = createLogger({ level: "info" });

logger.info("Server started", { port: 3000 });
logger.warn("Disk usage high", { usedPercent: 91 });
logger.error("Request failed", { path: "/api/user", statusCode: 500 });

Call createLogger() with no arguments anywhere else — it returns the same singleton.

// some-other-file.ts
const logger = createLogger(); // same instance, no config needed

Setup by Platform

Node / Express

// src/index.ts
const logger = createLogger({ level: "info" });

React Native

// App.tsx
import { createLogger, ConsoleTransport } from "@devraj-labs/logger";

createLogger({
  level:      __DEV__ ? "debug" : "warn",
  transports: [new ConsoleTransport({ format: __DEV__ ? "pretty" : "json" })],
});

Child Loggers

Scope a logger to a module or request. All context fields are stamped on every log line automatically.

const logger = createLogger();

// Module-scoped
const log = logger.child({ module: "AuthService" });
log.info("Token issued", { userId: "u_123" });
// → { module: "AuthService", userId: "u_123", message: "Token issued", ... }

// Request-scoped (nest deeper)
const reqLog = log.child({ requestId: "req_abc" });
reqLog.debug("Cache miss");
// → { module: "AuthService", requestId: "req_abc", message: "Cache miss", ... }

Log Levels

logger.debug("DB query", { sql: "SELECT ..." });   // internal details
logger.info("User signed in", { userId: "u_1" });  // normal events
logger.warn("Rate limit approaching", { pct: 80 }); // degraded but alive
logger.error("Payment failed", { orderId: "o_9" }); // operation failed
logger.fatal("Out of memory");                       // app about to crash

| Level | Numeric | When to use | |---------|---------|-------------| | debug | 0 | Internal details — queries, state changes | | info | 1 | Normal events — server started, user logged in | | warn | 2 | Something looks wrong but app is still running | | error | 3 | A request or operation failed | | fatal | 4 | App is about to crash | | silent| 5 | Disables all output |

Change the level at runtime:

logger.setLevel("warn"); // drop debug + info from here on

Transports

The logger never stores or sends anything itself. Transports decide what happens to each log entry.

Your Code → Logger (level gate) → ConsoleTransport   (prints to terminal)
                                → BatchTransport      (buffers → POST to server)
                                → YourCustomTransport (AsyncStorage / Sentry / file / anything)

ConsoleTransport

Added automatically if you don't specify any transports. Prints pretty output in dev, JSON in production.

import { ConsoleTransport } from "@devraj-labs/logger";

createLogger({
  transports: [
    new ConsoleTransport({ format: "pretty" }), // or "json" | "auto"
  ],
});

| Option | Type | Default | Description | |--------|------|---------|-------------| | format | "pretty" \| "json" \| "auto" | "auto" | Output format. "auto" picks pretty in dev, JSON in production | | fatalMethod | "error" \| "warn" \| "log" | "error" | Which console method is used for fatal entries |

BatchTransport

Buffers logs in memory and sends them in one batch. Avoids one HTTP call per log line.

import { BatchTransport } from "@devraj-labs/logger";

createLogger({
  transports: [
    new BatchTransport({
      sendBatch: async (entries) => {
        await fetch("/api/logs", {
          method: "POST",
          body: JSON.stringify(entries),
        });
      },
      maxBatchSize: 50,
      flushIntervalMs: 5000,
    }),
  ],
});

Custom Transport

Implement { name, log(entry) } — that's the whole interface.

import { Transport, LogEntry } from "@devraj-labs/logger";

class SentryTransport implements Transport {
  name = "sentry";
  minLevel = LogLevel.ERROR; // only errors and above

  log(entry: LogEntry): void {
    Sentry.captureMessage(entry.message, {
      level: entry.levelName,
      extra: entry.meta,
    });
  }
}

createLogger({
  transports: [new ConsoleTransport(), new SentryTransport()],
});

Per-transport level filtering

createLogger({
  level: "debug", // root logger emits everything
  transports: [
    new ConsoleTransport(),                              // sees all levels
    new SentryTransport({ minLevel: LogLevel.ERROR }),   // only errors+
  ],
});

Graceful Shutdown

If BatchTransport holds unsent logs when the process exits, they're lost. Call flush() before exiting.

process.on("SIGTERM", async () => {
  await logger.flush(); // drains all transport buffers
  process.exit(0);
});

Environment Defaults

| NODE_ENV | Default level | Console format | |---------------|---------------|------------------| | development | debug | pretty (colours) | | production | warn | JSON | | unset | debug | pretty |

Override at any time:

createLogger({ level: "info", forceJsonOutput: true });

API Reference

createLogger(config?)

Returns the singleton Logger. Config only applies on the first call.

| Option | Type | Default | Description | |--------|------|---------|-------------| | level | LogLevelName | env-detected | Minimum level to emit | | transports | Transport[] | [ConsoleTransport] | Active transports | | context | LogContext | {} | Global context merged into every entry | | forceJsonOutput | boolean | false | Force JSON format regardless of env |

Logger

| Method | Description | |--------|-------------| | debug / info / warn / error / fatal(msg, meta?) | Emit a log entry | | child(context) | Create a scoped child logger | | setLevel(level) | Change minimum level at runtime | | getLevel() | Returns current level name | | addTransport(transport) | Register a transport | | removeTransport(name) | Remove a transport by name (calls flush) | | setContext(context) | Merge context into root logger | | flush() | Drain all transport buffers |


Examples

| # | What it covers | File | |---|----------------|------| | 1 | All 5 log levels and terminal output | 01-basic-logging.ts | | 2 | setLevel() — which logs are dropped vs shown | 02-level-filtering.ts | | 3 | Child loggers — module name, nested request context | 03-child-logger.ts | | 4 | Production JSON output format | 04-json-output.ts | | 5 | Writing a custom transport, per-transport minLevel | 05-custom-transport.ts | | 6 | BatchTransport — buffer logs, send in one go | 06-batch-transport.ts | | 7 | flush() — don't lose logs on process exit | 07-flush-on-shutdown.ts | | 8 | Singleton — sharing one logger across multiple files | 08-singleton-across-files.ts |


License

MIT © devraj-labs