release: WRNexusJS 0.8.0
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
export type LogLevel = "debug" | "info" | "warn" | "error";
|
||||
|
||||
export interface LogRecord {
|
||||
timestamp: string;
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
service: string;
|
||||
traceId?: string;
|
||||
spanId?: string;
|
||||
requestId?: string;
|
||||
attributes: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface StructuredLogger {
|
||||
log(level: LogLevel, message: string, attributes?: Record<string, unknown>): void;
|
||||
debug(message: string, attributes?: Record<string, unknown>): void;
|
||||
info(message: string, attributes?: Record<string, unknown>): void;
|
||||
warn(message: string, attributes?: Record<string, unknown>): void;
|
||||
error(message: string, attributes?: Record<string, unknown>): void;
|
||||
child(attributes: Record<string, unknown>): StructuredLogger;
|
||||
}
|
||||
|
||||
export interface StructuredLoggerOptions {
|
||||
service?: string;
|
||||
level?: LogLevel;
|
||||
now?: () => Date;
|
||||
sink?: (record: LogRecord, line: string) => void;
|
||||
redact?: readonly string[];
|
||||
attributes?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const LEVELS: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };
|
||||
|
||||
function scrub(value: Record<string, unknown>, keys: Set<string>): Record<string, unknown> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, item]) => [
|
||||
key,
|
||||
keys.has(key.toLowerCase()) ? "[REDACTED]" : item,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
export function createStructuredLogger(options: StructuredLoggerOptions = {}): StructuredLogger {
|
||||
const threshold = LEVELS[options.level ?? "info"];
|
||||
const now = options.now ?? (() => new Date());
|
||||
const sink = options.sink ?? ((_record, line) => console.log(line));
|
||||
const redacted = new Set(
|
||||
(options.redact ?? ["authorization", "cookie", "password", "secret", "token"]).map((key) =>
|
||||
key.toLowerCase(),
|
||||
),
|
||||
);
|
||||
const base = { ...(options.attributes ?? {}) };
|
||||
const logger = (attributes: Record<string, unknown>): StructuredLogger => {
|
||||
const log = (level: LogLevel, message: string, values: Record<string, unknown> = {}) => {
|
||||
if (LEVELS[level] < threshold) return;
|
||||
const combined = scrub({ ...attributes, ...values }, redacted);
|
||||
const record: LogRecord = {
|
||||
timestamp: now().toISOString(),
|
||||
level,
|
||||
message,
|
||||
service: options.service ?? "wrnexus",
|
||||
...(typeof combined.traceId === "string" ? { traceId: combined.traceId } : {}),
|
||||
...(typeof combined.spanId === "string" ? { spanId: combined.spanId } : {}),
|
||||
...(typeof combined.requestId === "string" ? { requestId: combined.requestId } : {}),
|
||||
attributes: combined,
|
||||
};
|
||||
sink(record, JSON.stringify(record));
|
||||
};
|
||||
return {
|
||||
log,
|
||||
debug: (message, values) => log("debug", message, values),
|
||||
info: (message, values) => log("info", message, values),
|
||||
warn: (message, values) => log("warn", message, values),
|
||||
error: (message, values) => log("error", message, values),
|
||||
child: (values) => logger({ ...attributes, ...values }),
|
||||
};
|
||||
};
|
||||
return logger(base);
|
||||
}
|
||||
Reference in New Issue
Block a user