Add createRootLogger factory with transport selection by env
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
import { describe, expect, it, vi, afterEach } from "vitest";
|
||||||
|
|
||||||
|
describe("createRootLogger", () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
const mod = await import("./root.js");
|
||||||
|
mod.__resetRootLoggerForTests();
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a Logger with console transport in development", async () => {
|
||||||
|
process.env["NODE_ENV"] = "development";
|
||||||
|
const { createRootLogger, __resetRootLoggerForTests } = await import("./root.js");
|
||||||
|
__resetRootLoggerForTests();
|
||||||
|
const logger = createRootLogger();
|
||||||
|
expect(logger).toBeDefined();
|
||||||
|
expect(typeof logger.info).toBe("function");
|
||||||
|
expect(typeof logger.child).toBe("function");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a Logger with JSON-lines transport in production", async () => {
|
||||||
|
process.env["NODE_ENV"] = "production";
|
||||||
|
process.env["LOGS_ENDPOINT"] = "https://logs.example/ingest";
|
||||||
|
const { createRootLogger, __resetRootLoggerForTests } = await import("./root.js");
|
||||||
|
__resetRootLoggerForTests();
|
||||||
|
const logger = createRootLogger();
|
||||||
|
expect(logger).toBeDefined();
|
||||||
|
expect(typeof logger.info).toBe("function");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("caches the logger instance (returns same object on repeated calls)", async () => {
|
||||||
|
process.env["NODE_ENV"] = "development";
|
||||||
|
const { createRootLogger, __resetRootLoggerForTests } = await import("./root.js");
|
||||||
|
__resetRootLoggerForTests();
|
||||||
|
const a = createRootLogger();
|
||||||
|
const b = createRootLogger();
|
||||||
|
expect(a).toBe(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("child() produces a Logger with merged context", async () => {
|
||||||
|
process.env["NODE_ENV"] = "development";
|
||||||
|
const spy = vi.spyOn(console, "info").mockImplementation(() => {});
|
||||||
|
const { createRootLogger, __resetRootLoggerForTests } = await import("./root.js");
|
||||||
|
__resetRootLoggerForTests();
|
||||||
|
const logger = createRootLogger();
|
||||||
|
const child = logger.child({ traceId: "test-123" });
|
||||||
|
child.info("hello");
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(spy.mock.calls[0]?.[0]).toContain("test-123");
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type { Logger, LogTransport } from "./types.js";
|
||||||
|
import { LoggerImpl } from "./logger-impl.js";
|
||||||
|
import { ConsoleTransport } from "./console-transport.js";
|
||||||
|
import { JsonLinesHttpTransport } from "./json-lines-transport.js";
|
||||||
|
|
||||||
|
let cached: Logger | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates or returns the cached root logger. In development, uses
|
||||||
|
* ConsoleTransport. In other envs, uses JsonLinesHttpTransport if
|
||||||
|
* LOGS_ENDPOINT is set, otherwise falls back to console.
|
||||||
|
*/
|
||||||
|
export function createRootLogger(): Logger {
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
const env = process.env["NODE_ENV"] ?? "development";
|
||||||
|
const logsEndpoint = process.env["LOGS_ENDPOINT"];
|
||||||
|
|
||||||
|
let transport: LogTransport;
|
||||||
|
|
||||||
|
if (env === "development" || !logsEndpoint) {
|
||||||
|
transport = new ConsoleTransport();
|
||||||
|
} else {
|
||||||
|
transport = new JsonLinesHttpTransport({
|
||||||
|
endpoint: logsEndpoint,
|
||||||
|
batchSize: 50,
|
||||||
|
flushIntervalMs: 5000,
|
||||||
|
maxBufferSize: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = new LoggerImpl(transport);
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test-only: clears the cached root logger. */
|
||||||
|
export function __resetRootLoggerForTests(): void {
|
||||||
|
cached = undefined;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user