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

@bksystems/structured-tree-log

v2.1.0

Published

Winston-style structured logger for Node (ESM/CJS) with hierarchical trace trees, history output, and optional rotated file transport.

Readme

@bksystems/structured-tree-log

Winston-style structured logger for Node (ESM + CommonJS). Each log emits a header line, a JSON context block, and an indented history tree from child() breadcrumbs. Optional file transport with size rotation.

Install

npm install @bksystems/structured-tree-log

Requires Node 18+ (uses node:crypto, node:fs, node:path only — no runtime dependencies).

Import

import createLogger, { LEVELS, structuredTreeLog } from "@bksystems/structured-tree-log";

The default export is the same named export createLogger.

CommonJS

const { createLogger, LEVELS, structuredTreeLog } = require("@bksystems/structured-tree-log");

Quick start

import { createLogger } from "@bksystems/structured-tree-log";

const log = createLogger({
  service: "my-app",
  minLevel: "info",
});

log.info("Server listening", { port: 3000 });

const job = log.child({ method: "worker", action: "process" });
job.debug("tick", { n: 1 });

Log levels

error (0) → warninfohttpverbosedebugsilly (6). Lower index = higher priority. A log prints if its level rank is the configured minLevel.

createLogger(options)

| Option | Description | | ------------- | ---------------------------------------------------------------- | | service | Label in the header (default structured-tree-log). | | minLevel | One of the level names above (default info). | | traceId | Root trace segment; if omitted, a UUID is generated. | | baseContext | Object merged into every emit’s context (shallow). | | history | Initial breadcrumb frames (array of plain objects). | | file | Optional file transport — see File transport. |

API returned by createLogger

  • error, warn, info, http, verbose, debug, silly(message, context?)
  • log(level, message, context?) — level as name or numeric rank
  • child(part) — extend history; part is a string (item bind shorthand) or a plain object frame
  • setMinLevel(level), getMinLevel(), getTraceId(), getHistory()

Record shape (each emit)

  1. Header[ISO8601] [service] [LEVEL] message (message only; no trace in the header).
  2. JSON{ "traceId": "…", "context": { … } } where context is baseContext merged with the per-call context. traceId is not duplicated inside context.
  3. History tree — one line per history frame, optional └ result → …, optional sub-step lines under a frame.

Hierarchical traceId

Every child() appends /<newUuid> to the parent traceId, e.g.
root-uuid/child-uuid/grandchild-uuid. Parallel children from the same parent share the same prefix and differ in the last segment.

History tree rules

  • * line — Structural fields only: method, action, step / stepOf, itemId, plus any extra keys (not lineMessage, result, subSteps).
  • └ result → … — Uses the first info / warn / error message stored as lineMessage on that frame. Log context.result is not shown in the tree (still appears in JSON context if you pass it).
  • item bind (action: 'item', no step) — If there is no lineMessage, can show the id from child({ result }).
  • Pipeline steps (frames with step + stepOf, not action: 'batch') — Share the same * column as the preceding item frame (siblings under the same visual block).
  • Sub-steps — If the log context includes subStep, an entry is appended to the current leaf’s subSteps and printed as a mini step: * … › sub <id> then └ result → <message>.

Depth uses two spaces per level; a frame with a line advances depth by two for following generic frames (see source comments).

File transport

Enable with file: { dirname, … }. The directory is created if missing. Each console record (header + JSON + tree lines) is written as one appended block (same content as stdout).

| Field | Description | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | dirname | Required to enable file logging (absolute or relative path). | | filename | Template (default %{SERVICE}%-%DATE%.log). Placeholders: %DATE%, %{DATE}%, %{SERVICE}% (sanitized). %DATE% is UTC YYYY-MM-DD. | | maxSize / maxsize | Max size of the current file before rotation: number (bytes) or string (512k, 10m, 1g). Omit for no size rotation. | | maxFiles | The current segment is always name.000.log (first file of the day for a %DATE% template). Omit → keep 5 older segments (name.001.logname.005.log). null, false, 0, Infinity, or 'unlimited'no cap. Index 0–999 uses three digits (000999); from 1000 onward the width is natural (name.1000.log, …). Positive integer → that many archived segments (not counting .000). |

Writers are cached per (dirname, filename template, service, maxSize, maxFiles) so child() loggers with the same file config share one rotator.

Example with file + debug

import path from "node:path";
import { fileURLToPath } from "node:url";
import { createLogger } from "@bksystems/structured-tree-log";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const log = createLogger({
  service: "my-service",
  minLevel: "debug",
  file: {
    dirname: path.join(__dirname, "output"),
    filename: "%{SERVICE}%-%DATE%.log",
    maxSize: "50m",
    maxFiles: 14,
  },
  history: [{ method: "main", action: "bootstrap", result: "my-service" }],
});

Exports

  • createLogger — factory
  • LEVELS — level name → rank map
  • structuredTreeLog — pre-built default instance
  • default — same as createLogger

Package layout (this repo)

| Path | Role | | -------------- | ------------------------------------------------------------ | | index.ts | TypeScript source (compiled to dist/) | | dist/ | Build output (.js, .d.ts, source maps) — not committed | | LICENSE | MIT | | CHANGELOG.md | Release notes | | examples/ | Runnable examples (after npm run build + local path) |

Development

npm install
npm run build       # tsc → dist/
npm run verify      # build + run examples/quickstart.mjs against dist/
npm pack            # runs prepack → build, then creates .tgz (inspect contents)
npm run clean       # remove dist/

Publish (maintainers)

  1. npm login — account must be allowed to publish @bksystems/* (org member or owner).
  2. Set "author" in package.json if you want it on the npm page.
  3. npm pack — confirm the tarball lists package.json, dist/*, README.md, LICENSE, CHANGELOG.md (no src/).
  4. npm publish --access public — enable 2FA / token policy required by npm for publishing.

Bump version with npm version patch|minor|major before subsequent publishes.