@kleb/logging (0.2.0)
Installation
@kleb:registry=npm install @kleb/logging@0.2.0"@kleb/logging": "0.2.0"About this package
@kleb/logging
Structured logging for TypeScript projects.
Install from the private Forgejo npm registry after configuring auth for
https://git.kleb.sh/api/packages/kleb/npm/:
bun add @kleb/logging
import { createLogger } from "@kleb/logging";
const logger = createLogger();
logger.info("app.started", { port: 3000 });
Use the server entrypoint for rotating file logs:
import { createLogger } from "@kleb/logging";
import { createRotatingFileSink } from "@kleb/logging/server";
const logger = createLogger({
sinks: [
createRotatingFileSink({
directory: "logs",
archiveDirectory: "archive",
formats: ["json", "text"],
rotation: { every: "day", atHour: 0 },
maxArchived: 31,
}),
],
});
Archives are written below the active log directory by default:
logs/
current.log
current.json.log
error.log
errors.json.log
archive/
text/
json/
Use a custom archive folder when active logs and archived logs should be separated:
createRotatingFileSink({
directory: "logs/active",
archiveDirectory: "../archive",
formats: ["json", "text"],
rotation: { every: "hour" },
});
For short-lived validation or interval-based rotation, use rotation.every.milliseconds:
createRotatingFileSink({
directory: "logs",
formats: ["json", "text"],
rotation: { every: { milliseconds: 60_000 } },
});
Disable automatic rotation while keeping manual rotation available:
const sink = createRotatingFileSink({
directory: "logs",
rotation: false,
});
await sink.rotate();
Use the context entrypoint to attach request-scoped fields across async calls:
import { createLogger } from "@kleb/logging";
import { logContextProvider, withLogContext } from "@kleb/logging/context";
const logger = createLogger({
contextProvider: logContextProvider,
});
await withLogContext({ requestId: "req-123", userId: "user-1" }, async () => {
await doWork();
logger.info("work.completed");
});
Fields passed directly to a log call override active context fields:
logger.info("work.completed", { requestId: "req-explicit" });
Combine context providers when logs should include multiple ambient sources:
import { combineLogContextProviders, createLogger } from "@kleb/logging";
import { logContextProvider } from "@kleb/logging/context";
import { otelContextProvider } from "@kleb/logging/otel";
const logger = createLogger({
contextProvider: combineLogContextProviders(logContextProvider, otelContextProvider()),
});
The OpenTelemetry entrypoint reads the active span from @opentelemetry/api and adds
traceId and spanId when a valid active span exists. It does not initialize telemetry,
create spans, or export logs.
Redaction is explicit:
const logger = createLogger({
redaction: {
keys: ["authorization", /password/i],
patterns: [/token-[a-z0-9]+/gi],
},
});
HTTP helpers require already-safe route patterns:
logger.info(
"http.request.completed",
createHttpLogFields({
requestId: "req-123",
method: "GET",
routePattern: "/api/users/:id",
status: 200,
durationMs: 12.5,
}),
);
Dependencies
Development dependencies
| ID | Version |
|---|---|
| @opentelemetry/api | ^1.9.1 |
Peer dependencies
| ID | Version |
|---|---|
| @opentelemetry/api | ^1.9.1 |