Add dev-mode ConsoleTransport for logger
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import type { LogRecord } from "./types.js";
|
||||||
|
import { ConsoleTransport } from "./console-transport.js";
|
||||||
|
|
||||||
|
describe("ConsoleTransport", () => {
|
||||||
|
it("pipes debug records to console.debug", () => {
|
||||||
|
const spy = vi.spyOn(console, "debug").mockImplementation(() => {});
|
||||||
|
const transport = new ConsoleTransport();
|
||||||
|
const record: LogRecord = { ts: "2025-01-01T00:00:00Z", level: "debug", msg: "hello", fields: {} };
|
||||||
|
transport.write(record);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(spy.mock.calls[0]?.[0]).toContain("hello");
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("pipes info records to console.info", () => {
|
||||||
|
const spy = vi.spyOn(console, "info").mockImplementation(() => {});
|
||||||
|
const transport = new ConsoleTransport();
|
||||||
|
transport.write({ ts: "2025-01-01T00:00:00Z", level: "info", msg: "info msg", fields: { key: "val" } });
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(spy.mock.calls[0]?.[0]).toContain("info msg");
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("pipes warn records to console.warn", () => {
|
||||||
|
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||||
|
const transport = new ConsoleTransport();
|
||||||
|
transport.write({ ts: "2025-01-01T00:00:00Z", level: "warn", msg: "w", fields: {} });
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("pipes error records to console.error", () => {
|
||||||
|
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||||
|
const transport = new ConsoleTransport();
|
||||||
|
transport.write({ ts: "2025-01-01T00:00:00Z", level: "error", msg: "e", fields: {} });
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("flush is a no-op that resolves immediately", async () => {
|
||||||
|
const transport = new ConsoleTransport();
|
||||||
|
await expect(transport.flush()).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import type { LogRecord, LogTransport } from "./types.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dev-mode transport that pipes log records to the browser/Node console.
|
||||||
|
* Each record is printed as `[LEVEL] ts msg {fields}`.
|
||||||
|
*/
|
||||||
|
export class ConsoleTransport implements LogTransport {
|
||||||
|
write(record: LogRecord): void {
|
||||||
|
const prefix = `[${record.level.toUpperCase()}] ${record.ts}`;
|
||||||
|
const hasFields = Object.keys(record.fields).length > 0;
|
||||||
|
const msg = hasFields
|
||||||
|
? `${prefix} ${record.msg} ${JSON.stringify(record.fields)}`
|
||||||
|
: `${prefix} ${record.msg}`;
|
||||||
|
|
||||||
|
switch (record.level) {
|
||||||
|
case "debug":
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.debug(msg);
|
||||||
|
break;
|
||||||
|
case "info":
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.info(msg);
|
||||||
|
break;
|
||||||
|
case "warn":
|
||||||
|
console.warn(msg);
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
console.error(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async flush(): Promise<void> {
|
||||||
|
// Console output is synchronous — nothing to flush.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user