Skip to content

devraj-labs/logger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

Releases

No releases published

Packages

 
 
 

Contributors