mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
One stderr notice per invocation and _meta.deprecated: true in JSON mode (shared helper next to withMeta; layout owns both inspect and layout via createInspectCommand). Help descriptions gain the pointer. No behavior change; removal ships separately once migration telemetry says usage has decayed. fix(producer): route info/debug logs to stderr — the compiler's 'Localized remote media' line was landing on stdout ahead of validate's --json payload, breaking every piped consumer. Diagnostics now share stderr with warn/error; render progress uses its own channel.
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
/**
|
|
* Pluggable Producer Logger
|
|
*
|
|
* Lightweight pluggable logger with zero dependencies.
|
|
* Default implementation writes to console with level filtering.
|
|
*
|
|
* All levels write to stderr (console.error/console.warn), never stdout —
|
|
* producer runs inside CLI commands whose stdout is a machine-readable
|
|
* contract (e.g. `validate --json`, `check --json`); an info/debug line on
|
|
* stdout would corrupt that output. There is no diagnostic use case that
|
|
* needs these lines on stdout specifically, so the whole logger is stderr-only.
|
|
*
|
|
* Users can provide their own logger (e.g. Winston, Pino) by
|
|
* implementing the ProducerLogger interface.
|
|
*/
|
|
|
|
export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
|
|
export interface ProducerLogger {
|
|
error(message: string, meta?: Record<string, unknown>): void;
|
|
warn(message: string, meta?: Record<string, unknown>): void;
|
|
info(message: string, meta?: Record<string, unknown>): void;
|
|
debug(message: string, meta?: Record<string, unknown>): void;
|
|
|
|
/**
|
|
* Optional fast level check used to skip expensive metadata construction
|
|
* at the call site. When the call site needs to build a non-trivial meta
|
|
* object (e.g. snapshot a struct, format numbers, run `Array.find` over
|
|
* scene state) just to attach to a debug log, gate it with this method:
|
|
*
|
|
* ```ts
|
|
* if (log.isLevelEnabled?.("debug") ?? true) {
|
|
* const meta = buildExpensiveMeta();
|
|
* log.debug("hot-path event", meta);
|
|
* }
|
|
* ```
|
|
*
|
|
* The default coalescence (`?? true`) preserves today's behavior for
|
|
* loggers that omit this method — they keep building the meta object as
|
|
* before. Custom integrations (Pino, Winston, structured loggers) should
|
|
* implement this to enable the optimization.
|
|
*/
|
|
isLevelEnabled?(level: LogLevel): boolean;
|
|
}
|
|
|
|
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
error: 0,
|
|
warn: 1,
|
|
info: 2,
|
|
debug: 3,
|
|
};
|
|
|
|
/**
|
|
* Create a console-based logger with level filtering.
|
|
*
|
|
* Messages at or below the configured level are printed;
|
|
* everything else is silently dropped.
|
|
*/
|
|
export function createConsoleLogger(level: LogLevel = "info"): ProducerLogger {
|
|
const threshold = LOG_LEVEL_PRIORITY[level];
|
|
|
|
const shouldLog = (msgLevel: LogLevel): boolean => LOG_LEVEL_PRIORITY[msgLevel] <= threshold;
|
|
|
|
const formatMeta = (meta?: Record<string, unknown>): string =>
|
|
meta ? ` ${JSON.stringify(meta)}` : "";
|
|
|
|
return {
|
|
error(message, meta) {
|
|
if (shouldLog("error")) {
|
|
console.error(`[ERROR] ${message}${formatMeta(meta)}`);
|
|
}
|
|
},
|
|
warn(message, meta) {
|
|
if (shouldLog("warn")) {
|
|
console.warn(`[WARN] ${message}${formatMeta(meta)}`);
|
|
}
|
|
},
|
|
info(message, meta) {
|
|
if (shouldLog("info")) {
|
|
console.error(`[INFO] ${message}${formatMeta(meta)}`);
|
|
}
|
|
},
|
|
debug(message, meta) {
|
|
if (shouldLog("debug")) {
|
|
console.error(`[DEBUG] ${message}${formatMeta(meta)}`);
|
|
}
|
|
},
|
|
isLevelEnabled(msgLevel) {
|
|
return shouldLog(msgLevel);
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Default logger singleton (level: "info"). */
|
|
export const defaultLogger: ProducerLogger = createConsoleLogger("info");
|