mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:51:35 +00:00
Feat/logger support log level validation0222 (#23436)
* 1、环境变量**:新增 `OPENCLAW_LOG_LEVEL`,可取值 `silent|fatal|error|warn|info|debug|trace`。设置后同时覆盖**文件日志**与**控制台**的级别,优先级高于配置文件。 2、启动参数**:在 `openclaw gateway run` 上新增 `--log-level <level>`,对该次进程同时生效于文件与控制台;未传时仍使用环境变量或配置文件。 * fix(logging): make log-level override global and precedence-safe --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -39,6 +39,11 @@ describe("argv helpers", () => {
|
||||
argv: ["node", "openclaw", "--profile", "work", "-v"],
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "root -v alias with log-level",
|
||||
argv: ["node", "openclaw", "--log-level", "debug", "-v"],
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "subcommand -v should not be treated as version",
|
||||
argv: ["node", "openclaw", "acp", "-v"],
|
||||
|
||||
@@ -2,7 +2,7 @@ const HELP_FLAGS = new Set(["-h", "--help"]);
|
||||
const VERSION_FLAGS = new Set(["-V", "--version"]);
|
||||
const ROOT_VERSION_ALIAS_FLAG = "-v";
|
||||
const ROOT_BOOLEAN_FLAGS = new Set(["--dev", "--no-color"]);
|
||||
const ROOT_VALUE_FLAGS = new Set(["--profile"]);
|
||||
const ROOT_VALUE_FLAGS = new Set(["--profile", "--log-level"]);
|
||||
const FLAG_TERMINATOR = "--";
|
||||
|
||||
export function hasHelpOrVersion(argv: string[]): boolean {
|
||||
|
||||
13
src/cli/log-level-option.test.ts
Normal file
13
src/cli/log-level-option.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseCliLogLevelOption } from "./log-level-option.js";
|
||||
|
||||
describe("parseCliLogLevelOption", () => {
|
||||
it("accepts allowed log levels", () => {
|
||||
expect(parseCliLogLevelOption("debug")).toBe("debug");
|
||||
expect(parseCliLogLevelOption(" trace ")).toBe("trace");
|
||||
});
|
||||
|
||||
it("rejects invalid log levels", () => {
|
||||
expect(() => parseCliLogLevelOption("loud")).toThrow("Invalid --log-level");
|
||||
});
|
||||
});
|
||||
12
src/cli/log-level-option.ts
Normal file
12
src/cli/log-level-option.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { InvalidArgumentError } from "commander";
|
||||
import { ALLOWED_LOG_LEVELS, type LogLevel, tryParseLogLevel } from "../logging/levels.js";
|
||||
|
||||
export const CLI_LOG_LEVEL_VALUES = ALLOWED_LOG_LEVELS.join("|");
|
||||
|
||||
export function parseCliLogLevelOption(value: string): LogLevel {
|
||||
const parsed = tryParseLogLevel(value);
|
||||
if (!parsed) {
|
||||
throw new InvalidArgumentError(`Invalid --log-level (use ${CLI_LOG_LEVEL_VALUES})`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { escapeRegExp } from "../../utils.js";
|
||||
import { hasFlag, hasRootVersionAlias } from "../argv.js";
|
||||
import { formatCliBannerLine, hasEmittedCliBanner } from "../banner.js";
|
||||
import { replaceCliName, resolveCliName } from "../cli-name.js";
|
||||
import { CLI_LOG_LEVEL_VALUES, parseCliLogLevelOption } from "../log-level-option.js";
|
||||
import { getCoreCliCommandsWithSubcommands } from "./command-registry.js";
|
||||
import type { ProgramContext } from "./context.js";
|
||||
import { getSubCliCommandsWithSubcommands } from "./register.subclis.js";
|
||||
@@ -54,6 +55,11 @@ export function configureProgramHelp(program: Command, ctx: ProgramContext) {
|
||||
.option(
|
||||
"--profile <name>",
|
||||
"Use a named profile (isolates OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH under ~/.openclaw-<name>)",
|
||||
)
|
||||
.option(
|
||||
"--log-level <level>",
|
||||
`Global log level override for file + console (${CLI_LOG_LEVEL_VALUES})`,
|
||||
parseCliLogLevelOption,
|
||||
);
|
||||
|
||||
program.option("--no-color", "Disable ANSI colors", false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Command } from "commander";
|
||||
import { setVerbose } from "../../globals.js";
|
||||
import { isTruthyEnvValue } from "../../infra/env.js";
|
||||
import type { LogLevel } from "../../logging/levels.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getCommandPath, getVerboseFlag, hasHelpOrVersion } from "../argv.js";
|
||||
import { emitCliBanner } from "../banner.js";
|
||||
@@ -22,6 +23,26 @@ function setProcessTitleForCommand(actionCommand: Command) {
|
||||
// Commands that need channel plugins loaded
|
||||
const PLUGIN_REQUIRED_COMMANDS = new Set(["message", "channels", "directory"]);
|
||||
|
||||
function getRootCommand(command: Command): Command {
|
||||
let current = command;
|
||||
while (current.parent) {
|
||||
current = current.parent;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function getCliLogLevel(actionCommand: Command): LogLevel | undefined {
|
||||
const root = getRootCommand(actionCommand);
|
||||
if (typeof root.getOptionValueSource !== "function") {
|
||||
return undefined;
|
||||
}
|
||||
if (root.getOptionValueSource("logLevel") !== "cli") {
|
||||
return undefined;
|
||||
}
|
||||
const logLevel = root.opts<Record<string, unknown>>().logLevel;
|
||||
return typeof logLevel === "string" ? (logLevel as LogLevel) : undefined;
|
||||
}
|
||||
|
||||
export function registerPreActionHooks(program: Command, programVersion: string) {
|
||||
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
||||
setProcessTitleForCommand(actionCommand);
|
||||
@@ -40,6 +61,10 @@ export function registerPreActionHooks(program: Command, programVersion: string)
|
||||
}
|
||||
const verbose = getVerboseFlag(argv, { includeDebug: true });
|
||||
setVerbose(verbose);
|
||||
const cliLogLevel = getCliLogLevel(actionCommand);
|
||||
if (cliLogLevel) {
|
||||
process.env.OPENCLAW_LOG_LEVEL = cliLogLevel;
|
||||
}
|
||||
if (!verbose) {
|
||||
process.env.NODE_NO_WARNINGS ??= "1";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user