spb/social-runtime-crawler
Public
TypeScript 91.8%
HTML 3.2%
JavaScript 3%
SQL 1.4%
CSS 0.7%
1/** Structured logger: one JSON line per record on stderr, human line on stdout when SRC_LOG_PRETTY=1. */2export type LogLevel = "debug" | "info" | "warn" | "error";34const LEVELS: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };5const minLevel = LEVELS[(process.env.SRC_LOG_LEVEL as LogLevel) ?? "info"] ?? 20;6const pretty = process.env.SRC_LOG_PRETTY !== "0";78export interface Logger {9 debug(msg: string, data?: Record<string, unknown>): void;10 info(msg: string, data?: Record<string, unknown>): void;11 warn(msg: string, data?: Record<string, unknown>): void;12 error(msg: string, data?: Record<string, unknown>): void;13 child(bindings: Record<string, unknown>): Logger;14}1516function emit(level: LogLevel, scope: string, bindings: Record<string, unknown>, msg: string, data?: Record<string, unknown>) {17 if (LEVELS[level] < minLevel) return;18 const rec = { t: new Date().toISOString(), level, scope, msg, ...bindings, ...(data ?? {}) };19 if (pretty) {20 const extra = { ...bindings, ...(data ?? {}) };21 const tail = Object.keys(extra).length ? " " + JSON.stringify(extra) : "";22 const line = `${rec.t.slice(11, 19)} ${level.toUpperCase().padEnd(5)} [${scope}] ${msg}${tail}`;23 (level === "error" || level === "warn" ? process.stderr : process.stdout).write(line + "\n");24 } else {25 process.stderr.write(JSON.stringify(rec) + "\n");26 }27}2829export function createLogger(scope: string, bindings: Record<string, unknown> = {}): Logger {30 return {31 debug: (m, d) => emit("debug", scope, bindings, m, d),32 info: (m, d) => emit("info", scope, bindings, m, d),33 warn: (m, d) => emit("warn", scope, bindings, m, d),34 error: (m, d) => emit("error", scope, bindings, m, d),35 child: (b) => createLogger(scope, { ...bindings, ...b }),36 };37}38