tslog is a free, open source monitoring & observability project written in TypeScript and released under MIT. It has 1,740 GitHub stars, 74 forks and 0 open issues, and was last pushed 7 days ago. On this registry it ranks #158 of 191 tracked projects in Monitoring & Observability, with 5 head-to-head comparisons available.

Beautiful logging experience for TypeScript

lang: Typescript License: MIT npm version CI: GitHub codecov.io code style: biome GitHub stars

Powerful, fast and expressive logging for TypeScript and JavaScript

tslog pretty output

[!NOTE] This is tslog v5. It is a deliberate, breaking redesign โ€” ESM-only, grouped settings, a new fields-first JSON shape, and middleware instead of overwrite.*. If you are on the 4.x line, 4.11.0 is a safe place to stay โ€” most of the v5 performance wins were back-ported there with zero breaking changes. Upgrade when you want the new capabilities. See Upgrading from v4?.

Highlights

  • ๐Ÿ— Universal โ€” one logger for Node.js, browser, Deno, Bun, workers and React Native
  • ๐Ÿงฑ Structured, fields-first JSON โ€” flat, observability-ready output that drops straight into log pipelines
  • ๐Ÿงญ Pretty by default, JSON optional โ€” colored in your terminal, uncolored when piped/CI; structured JSON is one opt-in away
  • ๐Ÿค– First-class support for agents & LLMs โ€” fields-first calls, agent/session correlation, an llms.txt, and OTel-GenAI presets; used by OpenClaw for agent logging
  • ๐ŸŒณ Tree-shakeable subpaths โ€” transports, presets and helpers ship as opt-in modules, sideEffects: false
  • ๐Ÿชถ Zero runtime dependencies โ€” nothing pulled into your bundle but tslog itself
  • ๐Ÿ‘ฎ Fully typed โ€” written in TypeScript 7, native ESM, accurate source-mapped line numbers
  • ๐Ÿ™Š Secret masking โ€” keys, JSONPath-lite paths, regex, and a hashing censor for correlation
  • ๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Sub-loggers with inheritance โ€” child() / getSubLogger() with merged settings and accumulated names
  • ๐Ÿ”Œ Pluggable transports & middleware โ€” per-transport level/format, a use() pipeline, flush() and disposal
  • ๐Ÿ–ฅ Interactive browser objects โ€” pretty.passObjectsNatively (on by default in browsers) keeps logged objects collapsible in DevTools
  • ๐Ÿค“ Pretty errors & stack traces โ€” structured, fully serializable, captured only when needed ("auto" for pretty, "off" for JSON)

Example

import { Logger } from "tslog";

// `new Logger()` is pretty everywhere: colorized in an
// interactive terminal, uncolored when piped/redirected/CI.
// Omit `type` for pretty; set "pretty" | "json" | "hidden".
const log = new Logger({ minLevel: "INFO" });

// Fields-first OR string-first โ€” both work:
log.info({ port: 3000 }, "server started");
log.info("server started");

// A child logger per request or agent โ€” name, settings and
// fields are inherited. `child(...)` aliases `getSubLogger(...)`:
const requestLog = log.getSubLogger({ name: "agent:planner" });
requestLog.info({ tool: "search", tokens: 318 }, "tool call done");
// JSON โ†’ {"message":"tool call done","level":"INFO","levelId":3,
//         "time":"โ€ฆ","tool":"search","tokens":318,
//         "_logMeta":{"v":5,"name":"agent:planner",โ€ฆ}}

// Keep secrets, PII and prompts out of your logs (grouped under `mask`):
const safeLog = new Logger({
  type: "json",
  mask: {
    keys: ["password", "apiKey", "token", "prompt"],
    paths: ["user.password", "*.token"],
  },
});
safeLog.info({ user: { name: "Ada", password: "hunter2" } });
// โ†’ {"user":{"name":"Ada","password":"[***]"},"level":"INFO", โ€ฆ} (a lone object spreads its fields โ€” no message key)

Become a Sponsor

Donations help me allocate more time for my open source work.

Install

tslog is published to npm as a single ESM package. How you pull it in depends on the runtime โ€” npm for Node.js, bun add for Bun, an npm: specifier (or import map) for Deno, and a CDN URL for the browser:

Runtime Install / import
Node.js npm install tslog โ†’ import { Logger } from "tslog";
Bun bun add tslog โ†’ import { Logger } from "tslog";
Deno no install step โ€” import { Logger } from "npm:tslog";
Browser no install step โ€” import { Logger } from "https://esm.sh/tslog";

The per-runtime details are below.

[!IMPORTANT] tslog v5 is ESM-only and requires Node.js โ‰ฅ 22 (5.0โ€“5.1 also ran on Node 20; stay on [email protected] if you are still on it). There is no CommonJS build and no require("tslog"). If you cannot move to ESM or off Node 16/18 yet, stay on [email protected] โ€” it keeps CJS, Node 16+ and the v4 JSON shape. See Upgrading from v4?.

Node.js

npm install tslog

Set "type": "module" in your package.json and run Node with source maps for accurate line numbers:

{
  "name": "NAME",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc -p .",
    "start": "node --enable-source-maps dist/index.js"
  },
  "dependencies": {
    "tslog": "^5"
  }
}

After building (npm run build), start your app with npm start.

To run TypeScript directly, use a current ESM-aware runner, e.g. node --enable-source-maps --import tsx src/index.ts.

Deno

There is no install step in Deno โ€” the npm: specifier pulls tslog from npm and caches it on first run (or add it to an import map / deno add npm:tslog if you prefer a bare "tslog" import):

// main.ts
import { Logger } from "npm:tslog";

const logger = new Logger();
logger.info("Hello from Deno");
deno run main.ts
# grant optional metadata access: deno run --allow-env main.ts

Bun

Add the package with Bun's own installer, then import it by name:

bun add tslog
// main.ts
import { Logger } from "tslog";

const logger = new Logger();
logger.info("Hello from Bun");
bun run main.ts

Browser

<script type="module">
  import { Logger } from "https://esm.sh/tslog";
  const logger = new Logger();
  logger.silly("I am a silly log.");
</script>

A prebuilt IIFE bundle is also published for `` usage, exposing the global window.tslog. In the browser, the default output renders pretty logs with CSS styling.

Bundle-size sensitive? import { Logger } from "tslog/slim" ships the same structured-JSON pipeline at less than half the size (~9.8KB gzip vs ~20.7KB) by leaving out masking, pretty output, and stack capture โ€” mask settings and type: "pretty" throw there instead of silently degrading. Both sizes are enforced by a CI budget (npm run check-bundle-size).

Enable TypeScript source-map support so tslog can point at the correct line in your source:

// tsconfig.json
{
  compilerOptions: {
    // <!-- here
    "inlineSourceMap": true,
    "target": "es2022",
  },
}

React Native

npm install tslog

Works out of the box on Hermes and JSC โ€” Metro resolves the react-native entry automatically. tslog detects React Native (_logMeta.runtime: "react-native", with the Hermes engine version when available), parses Hermes/JSC stack frames correctly, and defaults to pretty output in the Metro console.

Which build should I use?

One package, purpose-built distributions. For most apps the answer is simply tslog: the main entry adapts to where it runs (colorized pretty output in your terminal or devtools, uncolored pretty when piped, and opt-in flat JSON for production โ€” see Default type and colorization), so the same import covers development and production without config. The other builds exist for the situations where the default trade-offs don't fit:

Situation Import Why
Development (server, browser, RN) tslog Zero confi

readme truncated โ€” read the full docs on github

Frequently asked questions

Is tslog free to use?

tslog is open source under the MIT licence. There is no licence fee and no seat count โ€” you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does tslog do?

๐Ÿ“ tslog - Universal Logger for TypeScript and JavaScript

What is tslog written in?

tslog is primarily written in TypeScript. Its source is publicly available at https://github.com/fullstack-build/tslog, and it has 1,740 GitHub stars.