fix(logging): make logger import browser-safe
This commit is contained in:
parent
546e4d940a
commit
df3a19051d
70
src/logging/logger.browser-import.test.ts
Normal file
70
src/logging/logger.browser-import.test.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type LoggerModule = typeof import("./logger.js");
|
||||
|
||||
const originalGetBuiltinModule = (
|
||||
process as NodeJS.Process & { getBuiltinModule?: (id: string) => unknown }
|
||||
).getBuiltinModule;
|
||||
|
||||
async function importBrowserSafeLogger(params?: {
|
||||
resolvePreferredOpenClawTmpDir?: ReturnType<typeof vi.fn>;
|
||||
}): Promise<{
|
||||
module: LoggerModule;
|
||||
resolvePreferredOpenClawTmpDir: ReturnType<typeof vi.fn>;
|
||||
}> {
|
||||
vi.resetModules();
|
||||
const resolvePreferredOpenClawTmpDir =
|
||||
params?.resolvePreferredOpenClawTmpDir ??
|
||||
vi.fn(() => {
|
||||
throw new Error("resolvePreferredOpenClawTmpDir should not run during browser-safe import");
|
||||
});
|
||||
|
||||
vi.doMock("../infra/tmp-openclaw-dir.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../infra/tmp-openclaw-dir.js")>(
|
||||
"../infra/tmp-openclaw-dir.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolvePreferredOpenClawTmpDir,
|
||||
};
|
||||
});
|
||||
|
||||
Object.defineProperty(process, "getBuiltinModule", {
|
||||
configurable: true,
|
||||
value: undefined,
|
||||
});
|
||||
|
||||
const module = await import("./logger.js");
|
||||
return { module, resolvePreferredOpenClawTmpDir };
|
||||
}
|
||||
|
||||
describe("logging/logger browser-safe import", () => {
|
||||
afterEach(() => {
|
||||
vi.resetModules();
|
||||
vi.doUnmock("../infra/tmp-openclaw-dir.js");
|
||||
Object.defineProperty(process, "getBuiltinModule", {
|
||||
configurable: true,
|
||||
value: originalGetBuiltinModule,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not resolve the preferred temp dir at import time when node fs is unavailable", async () => {
|
||||
const { module, resolvePreferredOpenClawTmpDir } = await importBrowserSafeLogger();
|
||||
|
||||
expect(resolvePreferredOpenClawTmpDir).not.toHaveBeenCalled();
|
||||
expect(module.DEFAULT_LOG_DIR).toBe("/tmp/openclaw");
|
||||
expect(module.DEFAULT_LOG_FILE).toBe("/tmp/openclaw/openclaw.log");
|
||||
});
|
||||
|
||||
it("disables file logging when imported in a browser-like environment", async () => {
|
||||
const { module, resolvePreferredOpenClawTmpDir } = await importBrowserSafeLogger();
|
||||
|
||||
expect(module.getResolvedLoggerSettings()).toMatchObject({
|
||||
level: "silent",
|
||||
file: "/tmp/openclaw/openclaw.log",
|
||||
});
|
||||
expect(module.isFileLogLevelEnabled("info")).toBe(false);
|
||||
expect(() => module.getLogger().info("browser-safe")).not.toThrow();
|
||||
expect(resolvePreferredOpenClawTmpDir).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -3,7 +3,10 @@ import path from "node:path";
|
||||
import { Logger as TsLogger } from "tslog";
|
||||
import { getCommandPathWithRootOptions } from "../cli/argv.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
|
||||
import {
|
||||
POSIX_OPENCLAW_TMP_DIR,
|
||||
resolvePreferredOpenClawTmpDir,
|
||||
} from "../infra/tmp-openclaw-dir.js";
|
||||
import { readLoggingConfig } from "./config.js";
|
||||
import type { ConsoleStyle } from "./console.js";
|
||||
import { resolveEnvLogLevelOverride } from "./env-log-level.js";
|
||||
@ -12,7 +15,27 @@ import { resolveNodeRequireFromMeta } from "./node-require.js";
|
||||
import { loggingState } from "./state.js";
|
||||
import { formatLocalIsoWithOffset } from "./timestamps.js";
|
||||
|
||||
export const DEFAULT_LOG_DIR = resolvePreferredOpenClawTmpDir();
|
||||
type ProcessWithBuiltinModule = NodeJS.Process & {
|
||||
getBuiltinModule?: (id: string) => unknown;
|
||||
};
|
||||
|
||||
function canUseNodeFs(): boolean {
|
||||
const getBuiltinModule = (process as ProcessWithBuiltinModule).getBuiltinModule;
|
||||
if (typeof getBuiltinModule !== "function") {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return getBuiltinModule("fs") !== undefined;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveDefaultLogDir(): string {
|
||||
return canUseNodeFs() ? resolvePreferredOpenClawTmpDir() : POSIX_OPENCLAW_TMP_DIR;
|
||||
}
|
||||
|
||||
export const DEFAULT_LOG_DIR = resolveDefaultLogDir();
|
||||
export const DEFAULT_LOG_FILE = path.join(DEFAULT_LOG_DIR, "openclaw.log"); // legacy single-file path
|
||||
|
||||
const LOG_PREFIX = "openclaw";
|
||||
@ -71,6 +94,14 @@ function canUseSilentVitestFileLogFastPath(envLevel: LogLevel | undefined): bool
|
||||
}
|
||||
|
||||
function resolveSettings(): ResolvedSettings {
|
||||
if (!canUseNodeFs()) {
|
||||
return {
|
||||
level: "silent",
|
||||
file: DEFAULT_LOG_FILE,
|
||||
maxFileBytes: DEFAULT_MAX_LOG_FILE_BYTES,
|
||||
};
|
||||
}
|
||||
|
||||
const envLevel = resolveEnvLogLevelOverride();
|
||||
// Test runs default file logs to silent. Skip config reads and fallback load in the
|
||||
// common case to avoid pulling heavy config/schema stacks on startup.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user