g14o
Packages@g14o/logger

Examples

Child loggers, request IDs, timing, redaction, and cache injection.

Process lifecycle (start / success)

Use start to announce the beginning or progress of a process, and success when it completes:

logger.start("Migrating database", { version: 3 });
// ... work ...
logger.success("Migration complete", { rows: 1200 });

Both share the info severity tier, so they emit at the default "info" threshold.

Child loggers

const requestLogger = logger.child({ requestId: "req-123" });

requestLogger.info("handled", { status: 200 });
// meta: { requestId: "req-123", status: 200 }

Per-call meta overrides child bindings with the same key.

Request IDs

Use withRequestId to create a request-scoped child logger. When no id is passed, one is generated automatically:

const requestLogger = logger.withRequestId();
// or with an explicit id from middleware:
const requestLogger = logger.withRequestId(req.headers["x-request-id"]);

requestLogger.info("handled", { status: 200 });
// meta: { requestId: "...", status: 200 }

withRequestId is sugar over child({ requestId }) and works with all transports.

Timing

Measure elapsed time with time. Calling it starts a timer; the returned stop function logs at success level with durationMs in meta:

const stop = logger.time("Handled request", { route: "/users" });
// ... work ...
stop({ status: 200 });
// success: "Handled request" {"route":"/users","status":200,"durationMs":43}

Timing inherits child bindings, so it works naturally with request-scoped loggers:

const requestLogger = logger.withRequestId("req-123");
const stop = requestLogger.time("Request complete");
stop();
// meta: { requestId: "req-123", durationMs: 25 }

Redaction

Sensitive keys are redacted recursively (case-insensitive). Defaults are password, token, and authorization. Values become "[REDACTED]" without mutating the original metadata:

import { createLogger } from "@g14o/logger";

const logger = createLogger({
  redact: ["password", "token", "authorization", "ssn"],
});

logger.info("user created", {
  email: "a@b.com",
  password: "secret",
  nested: { token: "abc" },
});
// meta: { email: "a@b.com", password: "[REDACTED]", nested: { token: "[REDACTED]" } }

Multiple transports

const logger = createLogger({
  name: "api",
  formatOptions: { pretty: true },
  transports: [{ type: "console" }, { type: "json" }],
});

Every emitted record is written to each configured transport.

Inject into @g14o/cache

import { createCache } from "@g14o/cache";
import { createLogger } from "@g14o/logger";

const logger = createLogger({ name: "cache" });

export const { withCache } = createCache({ logger });

Logger is assignable to the broader logger shape accepted by @g14o/cache.

Browser usage

No configuration changes are required for browsers:

import { createLogger } from "@g14o/logger";

export const logger = createLogger({
  name: "app",
  formatOptions: { pretty: true },
  transports: [{ type: "console" }],
});

See Formatting for Node vs browser prefix differences.

On this page