Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
647c704
Restructure the logger into core, env and render modules
terehov Jul 2, 2026
d6ab74f
Add tree-shakeable subpath modules
terehov Jul 2, 2026
76f4538
Drop the CJS build and publish ESM only
terehov Jul 2, 2026
564be4a
Rewrite the test suite for the v5 API
terehov Jul 2, 2026
f6b06e3
Document v5 and add a migration guide
terehov Jul 2, 2026
9e1c01c
Validate settings keys against the grouped shape
terehov Jul 2, 2026
5ac53df
Precompile the JSON line and spread message-first fields
terehov Jul 2, 2026
1af363e
Trim per-log overhead and use native inspect in ESM
terehov Jul 2, 2026
2c8cec8
Point npm run bench at the actual benchmark script
terehov Jul 2, 2026
8e1801a
Make flush() cover async transport writes and scope disposal
terehov Jul 2, 2026
c06d798
Contain file-transport failures and drain on process exit
terehov Jul 2, 2026
f8176bc
Bound and retry HTTP log delivery
terehov Jul 2, 2026
383a422
Stop the worker transport from pinning or starving the process
terehov Jul 2, 2026
99993e6
Add bindings and typed custom level methods
terehov Jul 4, 2026
3391032
Detect React Native and parse Hermes and JSC stack frames
terehov Jul 4, 2026
90f1431
Resolve the real OS hostname for server JSON logs
terehov Jul 4, 2026
cdf2bd4
Let apps inject AsyncLocalStorage for context propagation
terehov Jul 4, 2026
97661b7
Treat NO_COLOR as a color switch, not a format switch
terehov Jul 4, 2026
e4f7d51
Point the legacy module field at the universal entry
terehov Jul 4, 2026
9aa649b
Sync the browser spec with the (string, object) spread shape
terehov Jul 4, 2026
6039f91
Inject masking, JSON rendering and validation through the entries
terehov Jul 4, 2026
1de26d9
Add tslog/slim, a structured JSON logger at half the size
terehov Jul 4, 2026
d996dae
Enforce gzip budgets for the slim and full browser bundles
terehov Jul 4, 2026
fee348a
Add a build-picker guide to the README
terehov Jul 4, 2026
744633f
Batch Node JSON output through a buffered stdout sink
terehov Jul 4, 2026
665fece
Add an injectable clock and configurable JSON time output
terehov Jul 4, 2026
6dfa49c
Emit real OTLP/JSON from the otel preset
terehov Jul 4, 2026
501f3ed
Serialize errors in pino's wire shape in the pino preset
terehov Jul 4, 2026
844462a
Isolate the transport format step and default hidden transports to JSON
terehov Jul 4, 2026
2c2cdc8
Document the buffered sink, time seam, and preset output changes
terehov Jul 4, 2026
5159f5c
Remove dead v4 modules and reconcile coverage config
terehov Jul 5, 2026
af3c293
Wire the Node environment provider onto core/meta.ts
terehov Jul 5, 2026
02558e4
Fix worker transport dispose after a failed spawn
terehov Jul 5, 2026
d19d43b
Reach 100% test coverage across the source tree
terehov Jul 5, 2026
c8c1da1
Add migration guides and Sentry/Better Stack integration docs
terehov Jul 5, 2026
0003b76
Cover Sentry Logs in the Sentry integration docs
terehov Jul 5, 2026
7c6ebb4
Fix polyfill %i/%f parsing, flushSync double-write, and inert coverag…
terehov Jul 5, 2026
8cacdfb
Trim padding tests, convert testable coverage ignores into real tests…
terehov Jul 5, 2026
a3eaf14
Extract the shared provider implementation into env/providerBase
terehov Jul 5, 2026
ef25020
Fix doubled host in browser stack fullFilePath
terehov Jul 5, 2026
52163ba
Report a structured droppedCount on http transport buffer drops
terehov Jul 5, 2026
b244db9
Fix stale doc claims and expand llms.txt API coverage
terehov Jul 5, 2026
ac89125
Highlight first-class agent and LLM support and OpenClaw usage
terehov Jul 7, 2026
5b88479
Publish the v4-to-v5 migration guide as a docs site page
terehov Jul 7, 2026
49e9588
Default to pretty output everywhere and make JSON opt-in
terehov Jul 7, 2026
3ac1900
Fix browser stack parser for Windows Vite /@fs/ drive-letter paths
terehov Jul 7, 2026
352d03f
Rename _meta to _logMeta
terehov Jul 8, 2026
fac0648
Remove the stale examples/ tree
terehov Jul 8, 2026
8323373
Reflow README and migration guide for narrower content columns
terehov Jul 8, 2026
e39c30d
Redesign the docs site with a dark theme
terehov Jul 8, 2026
cd077d0
Remove global Window.chrome type declaration
terehov Jul 8, 2026
1c10154
Resolve error stack positions through source maps on Node, Bun, and Deno
terehov Jul 8, 2026
a2304ec
Improve source-map resolution: binary search, fullFilePath consistenc…
terehov Jul 9, 2026
d496454
Keep fullFilePath absolute after a source-map remap
terehov Jul 10, 2026
6e262a5
Bump docs deps: astro 7, starlight 0.41, vite 8
terehov Jul 10, 2026
8234681
Add pretty.passObjectsNatively to pass raw objects to the console
terehov Jul 10, 2026
ebcaf3c
Add logger.if() conditional logging gate
terehov Jul 10, 2026
e71bcd9
Fix the Deno test runner so its tests actually run
terehov Jul 10, 2026
e6a7f99
Fix doc inaccuracies found by the pre-release audit
terehov Jul 10, 2026
fbf4af3
Enable pretty.passObjectsNatively by default in browsers
terehov Jul 11, 2026
e45130b
Update README and release notes for v5.0.0: add TypeScript types and …
terehov Jul 14, 2026
f761374
Document v5 late features across CHANGELOG, migration guide, and rele…
terehov Jul 14, 2026
fd1ccc3
fix(tests): restore CI on Node 20 for lifecycle and worker.runner suites
terehov Jul 14, 2026
bc17e21
fix(tests): clear NO_COLOR in vitest setup and explicitly enable pret…
terehov Jul 14, 2026
24a9988
Merge branch 'development' into v5
terehov Jul 14, 2026
e367fb9
ci: expand Node test matrix to 20/22/24/26/latest; bump harnesses to 22
terehov Jul 14, 2026
51cc454
fix(tests): switch worker.runner until() to time-based 10s timeout fo…
terehov Jul 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ name: CI

on:
push:
branches: [master, v4, development]
branches: [master, v4, v5, development]
pull_request:
branches: [master, development]
branches: [master, v4, v5, development]

jobs:
test-node:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [20.x]
node-version: ['20', '22', '24', '26', 'latest']

env:
CI: true
COVERAGE: ${{ matrix.node-version == '20.x' && true || false }}

steps:
- uses: actions/checkout@v5
Expand All @@ -26,10 +26,27 @@ jobs:
node-version: ${{ matrix.node-version }}
- run: npm ci

# Node test suite + coverage.
- name: Run coverage
# Fail fast if the AI-facing docs (llms.txt/RECIPES.md) drifted from the source.
- name: Check doc sync
run: npm run check-doc-sync

# Guard the gzip budgets of the slim and full browser bundles.
- name: Check bundle size
run: npm run check-bundle-size

# Run the full test suite on every supported Node version.
# Coverage + Codecov report only on the minimum supported version (20)
# to avoid redundant uploads and overhead.
- name: Run tests (with coverage)
if: matrix.node-version == '20'
run: npm run coverage

- name: Run tests
if: matrix.node-version != '20'
run: npm test

- name: Report coverage
if: matrix.node-version == '20'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -46,7 +63,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 22
- run: npm ci
- name: Install Playwright ${{ matrix.browser }}
run: npx playwright install --with-deps ${{ matrix.browser }}
Expand All @@ -59,7 +76,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 22
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
Expand All @@ -73,7 +90,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 22
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Install docs dependencies
run: npm ci
working-directory: docs
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ bower_components
# Playwright test results
/test-results/

# Deno lockfile (written by the deno test adapter run; jsr deps are pinned per-run in CI)
/deno.lock

# Agent config
# AGENTS.md and CLAUDE.md are shared with contributors (committed).
# Personal, machine-local overrides go in CLAUDE.local.md (never committed).
Expand Down
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"js/ts.tsdk.path": "node_modules/@typescript/native-preview"
}
128 changes: 94 additions & 34 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# AGENTS.md — tslog

## Project Overview

tslog is a TypeScript logger for Node.js, browsers, Deno, and Bun. It supports JSON and pretty-printed output, stack traces, error formatting, custom transports, and sub-loggers. Current version: 4.10.2.
tslog is a TypeScript logger for browsers, Node.js, Deno, and Bun, React Native, and workers. It supports JSON and pretty-printed output, stack traces, error formatting, custom transports, and sub-loggers. v5 adds an always-`pretty` default `type` (colorization and `pretty.passObjectsNatively` — on by default in real browsers — are the env-aware parts, never the format), flat fields-first JSON, async-context correlation, path/regex masking, a middleware chain, and pino/otel/genai presets. Current version: 5.x (the `5` line; `_logMeta.v` in JSON output is `5`).

### Settings are grouped (v5)

There are no flat settings keys. Configuration is organized into groups passed to `new Logger({ ... })`:

- Top-level: `type` (`"json" | "pretty" | "hidden"`, defaults to `"pretty"`), `name`, `minLevel` (name or 0..6), `prefix`, `customLevels`, `strictConfig`, `clock` (injectable `() => Date`).
- `mask: { keys, paths, regex, caseInsensitive, placeholder, censor }` — redact secrets/PII/prompts by key, dotted path (`*` = one segment), or regex.
- `json: { messageKey, levelKey, levelIdKey, timeKey, time, errorKey, numericLevel, stableKeyOrder }` — structured-output key names/shape (`time`: `"iso" | "epoch" | false | fn`).
- `pretty: { enabled, template, errorTemplate, style, timeZone, styles, levelMethod, inspectOptions, ... }`.
- `stack: { capture: "off" | "lazy" | "auto" | "full", internalFramePatterns }`.
- `meta: { property, attachContext }`.

New runtime surface: `getSubLogger`/`child` (aliases), `runInContext`/`getContext` (AsyncLocalStorage correlation), `attachTransport`, `use` (middleware), `flush`, `addLevel`, `isLevelEnabled`, `[Symbol.asyncDispose]`/`[Symbol.dispose]`, `Logger.fromEnv`, and the `defineConfig` helper. Presets ship as subpaths: `tslog/presets/pino`, `tslog/otel`, `tslog/presets/genai`; transports as `tslog/transports/{file,http,ringbuffer}`.

## Quick Reference

Expand All @@ -12,30 +23,30 @@ npm test # Run Vitest test suite
npm run test:browser # Run Playwright browser tests (Chromium, Firefox, WebKit)
npm run test:bun # Run Vitest under Bun
npm run test:deno # Build + run Deno test adapter
npm run build # Full build: types → ESM/CJS → browser bundle → prepare dist
npm run build # Full build: types → ESM → browser IIFE bundle → prepare dist
npm run lint # Biome lint check
npm run format # Biome format all files
npm run check # Biome lint + format (write)
npm run coverage # Run tests with coverage report
npm run dev-ts # Watch mode (nodemon + ts-node on examples)
```

## Build System

- **TypeScript (tsc)** for ESM (`dist/esm/`) and CJS (`dist/cjs/`) builds
- **esbuild** (`build.js`) for browser IIFE bundle (`dist/browser/`)
- **Type declarations** generated separately to `dist/types/`
- Dual ESM/CJS publishing with conditional exports in package.json
- `"type": "module"` — project uses ESM by default
- **ESM-only.** There is no CJS build and no `require("tslog")` — v5 dropped dual publishing.
- **tsgo** (`@typescript/native-preview`, the TypeScript 7 native compiler) emits the ESM output (`dist/esm/`) and the declaration files (`dist/types/`).
- **esbuild** (`build.js`) bundles the browser IIFE (`dist/browser/index.js`, global `tslog`) from `src/index.browser.ts`.
- `"type": "module"` — the project is ESM throughout.
- `npm run build` = `build-types` → `build-esm` → `build-browser` → `prepare-publish`.
- **Conditional exports** in `package.json` pick the entry per runtime: `node` → `index.node.js`, `browser`/`worker` → `index.browser.js`, `deno`/`bun`/`react-native`/`default` → `index.universal.js`. Subpaths (`tslog/transports/*`, `tslog/presets/*`, `tslog/otel`, `tslog/serializers`, `tslog/lite`, `tslog/slim`, `tslog/console`, `tslog/testing`, `tslog/pretty/box`, `tslog/throttle`, `tslog/cli`) are individually mapped and tree-shakeable (`sideEffects: false`, audited by `npm run audit-sideeffects`; gzip budgets for the slim/full browser bundles enforced by `npm run check-bundle-size`).
- The `tslog` bin (NDJSON pretty-printer) is `dist/esm/subpaths/cli.js`.

### Build configs

| Config | Purpose |
|---|---|
| `tsconfig.json` | Base (ES2020, strict, NodeNext) |
| `tsconfig.esm.json` | ESM output |
| `tsconfig.cjs.json` | CJS output |
| `tsconfig.types.json` | Declaration files only |
| `tsconfig.json` | Base (ES2022, strict, NodeNext) |
| `tsconfig.esm.json` | ESM output (`dist/esm/`) |
| `tsconfig.types.json` | Declaration files only (`dist/types/`) |

## Testing

Expand All @@ -61,25 +72,68 @@ npm run dev-ts # Watch mode (nodemon + ts-node on examples)

## Source Architecture

v5 is organized into `core/` (runtime-agnostic logic), `env/` (per-runtime environment providers), `internal/` (small shared helpers), `render/` (output), and `subpaths/` (tree-shakeable add-ons). The entry points differ only in which environment factory they bind; the `Logger` class itself is shared.

```
src/
├── BaseLogger.ts # Core logger: settings, masking, formatting, transports
├── index.ts # Logger class (extends BaseLogger), default log levels
├── index.browser.ts # Browser entry point (Safari detection, CSS styling)
├── interfaces.ts # All TypeScript interfaces and types
├── formatTemplate.ts # Template string replacement
├── prettyLogStyles.ts # Default pretty-print styles
└── internal/
├── environment.ts # Runtime detection (Node, browser, Deno, Bun, worker)
├── stackTrace.ts # Stack trace parsing
├── errorUtils.ts # Error chain collection
├── metaFormatting.ts # Metadata formatting for pretty output
└── jsonStringifyRecursive.ts # Circular-safe JSON serialization
├── index.ts # Logger class (extends BaseLogger) + shared exports
├── index.node.ts # Node entry — binds createNodeEnvironment
├── index.universal.ts # Deno/Bun/React Native/default entry
├── index.browser.ts # Browser/worker entry (Safari detect, CSS styling)
├── BaseLogger.ts # Core logger plumbing
├── interfaces.ts # All TypeScript interfaces and types
├── formatTemplate.ts # Template string replacement
├── formatNumberAddZeros.ts # Zero-padding for template date/time parts
├── urlToObj.ts # URL → plain-object serialization
├── core/ # Runtime-agnostic logic
│ ├── settings.ts # Grouped-settings normalization/merge + v4-key migration warnings
│ ├── config.ts # defineConfig + TslogConfigError
│ ├── pipeline.ts # log() → mask → logObj → meta → format → transport
│ ├── masking.ts # keys / paths / regex / censor masking
│ ├── logObj.ts # Build the structured log object
│ ├── meta.ts # _logMeta assembly
│ ├── levels.ts # DefaultLogLevels + custom levels
│ ├── transports.ts # Transport registry + isolation
│ ├── asyncContext.ts # runInContext / getContext (AsyncLocalStorage)
│ ├── fromEnv.ts # Logger.fromEnv (TSLOG_LEVEL/TYPE/NAME)
│ ├── levelPersistence.ts # persistLevel (browser localStorage)
│ ├── features.ts # CoreFeatures seam (what a build ships: masking, JSON renderer, …)
│ └── features.full.ts # Full feature set injected by the standard entries
├── env/ # Per-runtime environment providers
│ ├── environment.ts # EnvironmentProvider contract + factory types
│ ├── environment.node.ts # createNodeEnvironment
│ ├── environment.browser.ts# createBrowserEnvironment
│ ├── environment.universal.ts # createUniversalEnvironment / selectEnvironment
│ ├── environment.slim.ts # Minimal provider for tslog/slim
│ ├── providerBase.ts # Shared provider base (meta markup, transport formatting)
│ ├── shared.ts # Helpers shared across providers (paths, meta text)
│ ├── stackTrace.ts # Stack capture/parse
│ ├── sourceMap.node.ts # Source-map error-position resolution (Node/Bun/Deno)
│ └── stdoutSink.node.ts # Buffered stdout sink (Node JSON output)
├── internal/ # Small shared helpers
│ ├── environment.ts # Runtime detection, TTY/color probing, resolveDefaultType
│ ├── errorUtils.ts # Error introspection helpers
│ ├── exitHooks.ts # Process/page exit flushing for transports
│ ├── jsonStringifyRecursive.ts # Circular-safe JSON stringify
│ ├── metaFormatting.ts # Pretty meta template rendering
│ ├── nativeConsole.ts # Untouched console method access
│ └── InspectOptions.interface.ts # Runtime-free InspectOptions type
├── render/ # Output formatting
│ ├── json.ts # JSON serialization (flat, fields-first, _logMeta.v)
│ ├── inspect.ts # native util.inspect (Node)
│ ├── inspect.polyfill.ts # off-Node inspect polyfill
│ └── styles.ts # pretty styling
└── subpaths/ # Tree-shakeable add-ons (each its own export)
├── presets/{pino,otel,genai}.ts
├── transports/{file,http,ringBuffer,worker,worker.runner}.ts
├── serializers/std.ts ├── pretty/box.ts
├── lite.ts ├── slim.ts ├── testing.ts ├── throttle.ts
├── wrapConsole.ts └── cli.ts # tslog bin (NDJSON pretty-printer)
```

**Log lifecycle:** `log()` → mask → toLogObj → addMeta → format → transport
**Log lifecycle:** `log()` → mask → build logObj → attach `_logMeta` → middleware (`use`) → format → transports (`render/` formats; per-transport `format` can override).

**7 log levels:** silly(0), trace(1), debug(2), info(3), warn(4), error(5), fatal(6)
**7 default log levels:** silly(0), trace(1), debug(2), info(3), warn(4), error(5), fatal(6). Add more via the `customLevels` setting or `addLevel()`.

## Git Workflow

Expand All @@ -92,15 +146,15 @@ src/

- Uses **np** for releases (`npm run release`)
- Publishes from `dist/` directory (`publishConfig.directory: "dist"`)
- `scripts/prepare-publish.mjs` copies package.json/LICENSE/README into dist with adjusted paths
- Supports: Node.js (ESM/CJS), browsers (IIFE), Deno, Bun, React Native
- `scripts/prepare-publish.mjs` writes a dist-relative `package.json` and copies `LICENSE`, `README.md`, `llms.txt`, `RECIPES.md`, and `MIGRATION_v4_to_v5.md` into `dist/`
- Supports: Node.js (ESM), browsers (IIFE), Deno, Bun, React Native — all ESM, no CJS

## Key Conventions

- Node.js 16+ required (`.nvmrc`)
- Node.js 20+ required (`package.json` `engines`); ES2022 target
- npm only (engine-strict in `.npmrc`)
- No additional runtime dependencies
- TypeScript strict mode throughout
- Zero runtime dependencies
- TypeScript 7 (tsgo) strict mode throughout; ESM-only
- Tests are numbered by feature area (e.g., `1_json_loglevel`, `5_pretty_Log_Types`)
- Browser-specific code isolated in `index.browser.ts` and `tests/support/`

Expand All @@ -109,4 +163,10 @@ src/
- Target **100% test coverage** — but only with meaningful tests, no padding
- Every new feature **must** have corresponding tests
- Every new feature **must** be reflected in the docs (`docs/`)
- Don't write tests just to hit coverage numbers; each test should verify real behavior
- Don't write tests just to hit coverage numbers; each test should verify real behavior

### AI-facing docs (`llms.txt`, `RECIPES.md`)

- `llms.txt` (machine-readable API surface for code generation) and `RECIPES.md` (copy-paste patterns) are published with the package and must stay consistent with each other and with the public API. When you change a public setting/method or one of these files, update both — and the README — in the same change.
- `npm run check-doc-sync` (part of `npm run check`, run by the pre-commit hook and CI) enforces the mechanical invariants: every setting/symbol named in the docs exists in the source, the masking examples don't conflict, and relative links resolve.
- The script only checks structural drift. For semantic accuracy/completeness, run the deeper `audit-ai-docs` workflow before a release.
Loading
Loading