mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-03 03:03:24 -04:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -10,11 +10,15 @@ type LoggingConfig = OpenClawConfig["logging"];
|
||||
export function readLoggingConfig(): LoggingConfig | undefined {
|
||||
const configPath = resolveConfigPath();
|
||||
try {
|
||||
if (!fs.existsSync(configPath)) return undefined;
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return undefined;
|
||||
}
|
||||
const raw = fs.readFileSync(configPath, "utf-8");
|
||||
const parsed = json5.parse(raw);
|
||||
const logging = parsed?.logging;
|
||||
if (!logging || typeof logging !== "object" || Array.isArray(logging)) return undefined;
|
||||
if (!logging || typeof logging !== "object" || Array.isArray(logging)) {
|
||||
return undefined;
|
||||
}
|
||||
return logging as LoggingConfig;
|
||||
} catch {
|
||||
return undefined;
|
||||
|
||||
@@ -19,7 +19,9 @@ export type ConsoleLoggerSettings = ConsoleSettings;
|
||||
const requireConfig = createRequire(import.meta.url);
|
||||
|
||||
function normalizeConsoleLevel(level?: string): LogLevel {
|
||||
if (isVerbose()) return "debug";
|
||||
if (isVerbose()) {
|
||||
return "debug";
|
||||
}
|
||||
return normalizeLogLevel(level, "info");
|
||||
}
|
||||
|
||||
@@ -27,7 +29,9 @@ function normalizeConsoleStyle(style?: string): ConsoleStyle {
|
||||
if (style === "compact" || style === "json" || style === "pretty") {
|
||||
return style;
|
||||
}
|
||||
if (!process.stdout.isTTY) return "compact";
|
||||
if (!process.stdout.isTTY) {
|
||||
return "compact";
|
||||
}
|
||||
return "pretty";
|
||||
}
|
||||
|
||||
@@ -57,7 +61,9 @@ function resolveConsoleSettings(): ConsoleSettings {
|
||||
}
|
||||
|
||||
function consoleSettingsChanged(a: ConsoleSettings | null, b: ConsoleSettings) {
|
||||
if (!a) return true;
|
||||
if (!a) {
|
||||
return true;
|
||||
}
|
||||
return a.level !== b.level || a.style !== b.style;
|
||||
}
|
||||
|
||||
@@ -110,7 +116,9 @@ const SUPPRESSED_CONSOLE_PREFIXES = [
|
||||
] as const;
|
||||
|
||||
function shouldSuppressConsoleMessage(message: string): boolean {
|
||||
if (isVerbose()) return false;
|
||||
if (isVerbose()) {
|
||||
return false;
|
||||
}
|
||||
if (SUPPRESSED_CONSOLE_PREFIXES.some((prefix) => message.startsWith(prefix))) {
|
||||
return true;
|
||||
}
|
||||
@@ -130,7 +138,9 @@ function isEpipeError(err: unknown): boolean {
|
||||
|
||||
function formatConsoleTimestamp(style: ConsoleStyle): string {
|
||||
const now = new Date().toISOString();
|
||||
if (style === "pretty") return now.slice(11, 19);
|
||||
if (style === "pretty") {
|
||||
return now.slice(11, 19);
|
||||
}
|
||||
return now;
|
||||
}
|
||||
|
||||
@@ -140,7 +150,9 @@ function hasTimestampPrefix(value: string): boolean {
|
||||
|
||||
function isJsonPayload(value: string): boolean {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
|
||||
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
JSON.parse(trimmed);
|
||||
return true;
|
||||
@@ -154,7 +166,9 @@ function isJsonPayload(value: string): boolean {
|
||||
* This keeps user-facing output unchanged but guarantees every console call is captured in log files.
|
||||
*/
|
||||
export function enableConsoleCapture(): void {
|
||||
if (loggingState.consolePatched) return;
|
||||
if (loggingState.consolePatched) {
|
||||
return;
|
||||
}
|
||||
loggingState.consolePatched = true;
|
||||
|
||||
let logger: ReturnType<typeof getLogger> | null = null;
|
||||
@@ -184,7 +198,9 @@ export function enableConsoleCapture(): void {
|
||||
(level: LogLevel, orig: (...args: unknown[]) => void) =>
|
||||
(...args: unknown[]) => {
|
||||
const formatted = util.format(...args);
|
||||
if (shouldSuppressConsoleMessage(formatted)) return;
|
||||
if (shouldSuppressConsoleMessage(formatted)) {
|
||||
return;
|
||||
}
|
||||
const trimmed = stripAnsi(formatted).trimStart();
|
||||
const shouldPrefixTimestamp =
|
||||
loggingState.consoleTimestampPrefix &&
|
||||
@@ -219,7 +235,9 @@ export function enableConsoleCapture(): void {
|
||||
const line = timestamp ? `${timestamp} ${formatted}` : formatted;
|
||||
process.stderr.write(`${line}\n`);
|
||||
} catch (err) {
|
||||
if (isEpipeError(err)) return;
|
||||
if (isEpipeError(err)) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
@@ -238,7 +256,9 @@ export function enableConsoleCapture(): void {
|
||||
}
|
||||
orig.call(console, timestamp, ...args);
|
||||
} catch (err) {
|
||||
if (isEpipeError(err)) return;
|
||||
if (isEpipeError(err)) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,12 @@ function getSessionState(ref: SessionRef): SessionState {
|
||||
const key = resolveSessionKey(ref);
|
||||
const existing = sessionStates.get(key);
|
||||
if (existing) {
|
||||
if (ref.sessionId) existing.sessionId = ref.sessionId;
|
||||
if (ref.sessionKey) existing.sessionKey = ref.sessionKey;
|
||||
if (ref.sessionId) {
|
||||
existing.sessionId = ref.sessionId;
|
||||
}
|
||||
if (ref.sessionKey) {
|
||||
existing.sessionKey = ref.sessionKey;
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
const created: SessionState = {
|
||||
@@ -201,7 +205,9 @@ export function logSessionStateChange(
|
||||
const prevState = state.state;
|
||||
state.state = params.state;
|
||||
state.lastActivity = Date.now();
|
||||
if (params.state === "idle") state.queueDepth = Math.max(0, state.queueDepth - 1);
|
||||
if (params.state === "idle") {
|
||||
state.queueDepth = Math.max(0, state.queueDepth - 1);
|
||||
}
|
||||
if (!isProbeSession) {
|
||||
diag.debug(
|
||||
`session state: sessionId=${state.sessionId ?? "unknown"} sessionKey=${
|
||||
@@ -292,7 +298,9 @@ export function logActiveRuns() {
|
||||
let heartbeatInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
export function startDiagnosticHeartbeat() {
|
||||
if (heartbeatInterval) return;
|
||||
if (heartbeatInterval) {
|
||||
return;
|
||||
}
|
||||
heartbeatInterval = setInterval(() => {
|
||||
const now = Date.now();
|
||||
const activeCount = Array.from(sessionStates.values()).filter(
|
||||
@@ -311,8 +319,12 @@ export function startDiagnosticHeartbeat() {
|
||||
activeCount > 0 ||
|
||||
waitingCount > 0 ||
|
||||
totalQueued > 0;
|
||||
if (!hasActivity) return;
|
||||
if (now - lastActivityAt > 120_000 && activeCount === 0 && waitingCount === 0) return;
|
||||
if (!hasActivity) {
|
||||
return;
|
||||
}
|
||||
if (now - lastActivityAt > 120_000 && activeCount === 0 && waitingCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
diag.debug(
|
||||
`heartbeat: webhooks=${webhookStats.received}/${webhookStats.processed}/${webhookStats.errors} active=${activeCount} waiting=${waitingCount} queued=${totalQueued}`,
|
||||
|
||||
@@ -42,7 +42,9 @@ const externalTransports = new Set<LogTransport>();
|
||||
|
||||
function attachExternalTransport(logger: TsLogger<LogObj>, transport: LogTransport): void {
|
||||
logger.attachTransport((logObj: LogObj) => {
|
||||
if (!externalTransports.has(transport)) return;
|
||||
if (!externalTransports.has(transport)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
transport(logObj as LogTransportRecord);
|
||||
} catch {
|
||||
@@ -70,14 +72,20 @@ function resolveSettings(): ResolvedSettings {
|
||||
}
|
||||
|
||||
function settingsChanged(a: ResolvedSettings | null, b: ResolvedSettings) {
|
||||
if (!a) return true;
|
||||
if (!a) {
|
||||
return true;
|
||||
}
|
||||
return a.level !== b.level || a.file !== b.file;
|
||||
}
|
||||
|
||||
export function isFileLogLevelEnabled(level: LogLevel): boolean {
|
||||
const settings = (loggingState.cachedSettings as ResolvedSettings | null) ?? resolveSettings();
|
||||
if (!loggingState.cachedSettings) loggingState.cachedSettings = settings;
|
||||
if (settings.level === "silent") return false;
|
||||
if (!loggingState.cachedSettings) {
|
||||
loggingState.cachedSettings = settings;
|
||||
}
|
||||
if (settings.level === "silent") {
|
||||
return false;
|
||||
}
|
||||
return levelToMinLevel(level) <= levelToMinLevel(settings.level);
|
||||
}
|
||||
|
||||
@@ -223,8 +231,12 @@ function pruneOldRollingLogs(dir: string): void {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const cutoff = Date.now() - MAX_LOG_AGE_MS;
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
if (!entry.name.startsWith(`${LOG_PREFIX}-`) || !entry.name.endsWith(LOG_SUFFIX)) continue;
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
if (!entry.name.startsWith(`${LOG_PREFIX}-`) || !entry.name.endsWith(LOG_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
try {
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
@@ -10,7 +10,9 @@ export type ParsedLogLine = {
|
||||
function extractMessage(value: Record<string, unknown>): string {
|
||||
const parts: string[] = [];
|
||||
for (const key of Object.keys(value)) {
|
||||
if (!/^\d+$/.test(key)) continue;
|
||||
if (!/^\d+$/.test(key)) {
|
||||
continue;
|
||||
}
|
||||
const item = value[key];
|
||||
if (typeof item === "string") {
|
||||
parts.push(item);
|
||||
@@ -22,7 +24,9 @@ function extractMessage(value: Record<string, unknown>): string {
|
||||
}
|
||||
|
||||
function parseMetaName(raw?: unknown): { subsystem?: string; module?: string } {
|
||||
if (typeof raw !== "string") return {};
|
||||
if (typeof raw !== "string") {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||
return {
|
||||
|
||||
@@ -46,7 +46,9 @@ function normalizeMode(value?: string): RedactSensitiveMode {
|
||||
}
|
||||
|
||||
function parsePattern(raw: string): RegExp | null {
|
||||
if (!raw.trim()) return null;
|
||||
if (!raw.trim()) {
|
||||
return null;
|
||||
}
|
||||
const match = raw.match(/^\/(.+)\/([gimsuy]*)$/);
|
||||
try {
|
||||
if (match) {
|
||||
@@ -65,7 +67,9 @@ function resolvePatterns(value?: string[]): RegExp[] {
|
||||
}
|
||||
|
||||
function maskToken(token: string): string {
|
||||
if (token.length < DEFAULT_REDACT_MIN_LENGTH) return "***";
|
||||
if (token.length < DEFAULT_REDACT_MIN_LENGTH) {
|
||||
return "***";
|
||||
}
|
||||
const start = token.slice(0, DEFAULT_REDACT_KEEP_START);
|
||||
const end = token.slice(-DEFAULT_REDACT_KEEP_END);
|
||||
return `${start}…${end}`;
|
||||
@@ -73,16 +77,22 @@ function maskToken(token: string): string {
|
||||
|
||||
function redactPemBlock(block: string): string {
|
||||
const lines = block.split(/\r?\n/).filter(Boolean);
|
||||
if (lines.length < 2) return "***";
|
||||
if (lines.length < 2) {
|
||||
return "***";
|
||||
}
|
||||
return `${lines[0]}\n…redacted…\n${lines[lines.length - 1]}`;
|
||||
}
|
||||
|
||||
function redactMatch(match: string, groups: string[]): string {
|
||||
if (match.includes("PRIVATE KEY-----")) return redactPemBlock(match);
|
||||
if (match.includes("PRIVATE KEY-----")) {
|
||||
return redactPemBlock(match);
|
||||
}
|
||||
const token =
|
||||
groups.filter((value) => typeof value === "string" && value.length > 0).at(-1) ?? match;
|
||||
const masked = maskToken(token);
|
||||
if (token === match) return masked;
|
||||
if (token === match) {
|
||||
return masked;
|
||||
}
|
||||
return match.replace(token, masked);
|
||||
}
|
||||
|
||||
@@ -113,17 +123,25 @@ function resolveConfigRedaction(): RedactOptions {
|
||||
}
|
||||
|
||||
export function redactSensitiveText(text: string, options?: RedactOptions): string {
|
||||
if (!text) return text;
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
const resolved = options ?? resolveConfigRedaction();
|
||||
if (normalizeMode(resolved.mode) === "off") return text;
|
||||
if (normalizeMode(resolved.mode) === "off") {
|
||||
return text;
|
||||
}
|
||||
const patterns = resolvePatterns(resolved.patterns);
|
||||
if (!patterns.length) return text;
|
||||
if (!patterns.length) {
|
||||
return text;
|
||||
}
|
||||
return redactText(text, patterns);
|
||||
}
|
||||
|
||||
export function redactToolDetail(detail: string): string {
|
||||
const resolved = resolveConfigRedaction();
|
||||
if (normalizeMode(resolved.mode) !== "tools") return detail;
|
||||
if (normalizeMode(resolved.mode) !== "tools") {
|
||||
return detail;
|
||||
}
|
||||
return redactSensitiveText(detail, resolved);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ export type SubsystemLogger = {
|
||||
};
|
||||
|
||||
function shouldLogToConsole(level: LogLevel, settings: { level: LogLevel }): boolean {
|
||||
if (settings.level === "silent") return false;
|
||||
if (settings.level === "silent") {
|
||||
return false;
|
||||
}
|
||||
const current = levelToMinLevel(level);
|
||||
const min = levelToMinLevel(settings.level);
|
||||
return current <= min;
|
||||
@@ -35,7 +37,9 @@ type ChalkInstance = InstanceType<typeof Chalk>;
|
||||
|
||||
function isRichConsoleEnv(): boolean {
|
||||
const term = (process.env.TERM ?? "").toLowerCase();
|
||||
if (process.env.COLORTERM || process.env.TERM_PROGRAM) return true;
|
||||
if (process.env.COLORTERM || process.env.TERM_PROGRAM) {
|
||||
return true;
|
||||
}
|
||||
return term.length > 0 && term !== "dumb";
|
||||
}
|
||||
|
||||
@@ -44,7 +48,9 @@ function getColorForConsole(): ChalkInstance {
|
||||
typeof process.env.FORCE_COLOR === "string" &&
|
||||
process.env.FORCE_COLOR.trim().length > 0 &&
|
||||
process.env.FORCE_COLOR.trim() !== "0";
|
||||
if (process.env.NO_COLOR && !hasForceColor) return new Chalk({ level: 0 });
|
||||
if (process.env.NO_COLOR && !hasForceColor) {
|
||||
return new Chalk({ level: 0 });
|
||||
}
|
||||
const hasTty = Boolean(process.stdout.isTTY || process.stderr.isTTY);
|
||||
return hasTty || isRichConsoleEnv() ? new Chalk({ level: 1 }) : new Chalk({ level: 0 });
|
||||
}
|
||||
@@ -59,7 +65,9 @@ const CHANNEL_SUBSYSTEM_PREFIXES = new Set<string>(CHAT_CHANNEL_ORDER);
|
||||
|
||||
function pickSubsystemColor(color: ChalkInstance, subsystem: string): ChalkInstance {
|
||||
const override = SUBSYSTEM_COLOR_OVERRIDES[subsystem];
|
||||
if (override) return color[override];
|
||||
if (override) {
|
||||
return color[override];
|
||||
}
|
||||
let hash = 0;
|
||||
for (let i = 0; i < subsystem.length; i += 1) {
|
||||
hash = (hash * 31 + subsystem.charCodeAt(i)) | 0;
|
||||
@@ -78,7 +86,9 @@ function formatSubsystemForConsole(subsystem: string): string {
|
||||
) {
|
||||
parts.shift();
|
||||
}
|
||||
if (parts.length === 0) return original;
|
||||
if (parts.length === 0) {
|
||||
return original;
|
||||
}
|
||||
if (CHANNEL_SUBSYSTEM_PREFIXES.has(parts[0])) {
|
||||
return parts[0];
|
||||
}
|
||||
@@ -92,7 +102,9 @@ export function stripRedundantSubsystemPrefixForConsole(
|
||||
message: string,
|
||||
displaySubsystem: string,
|
||||
): string {
|
||||
if (!displaySubsystem) return message;
|
||||
if (!displaySubsystem) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// Common duplication: "[discord] discord: ..." (when a message manually includes the subsystem tag).
|
||||
if (message.startsWith("[")) {
|
||||
@@ -101,22 +113,34 @@ export function stripRedundantSubsystemPrefixForConsole(
|
||||
const bracketTag = message.slice(1, closeIdx);
|
||||
if (bracketTag.toLowerCase() === displaySubsystem.toLowerCase()) {
|
||||
let i = closeIdx + 1;
|
||||
while (message[i] === " ") i += 1;
|
||||
while (message[i] === " ") {
|
||||
i += 1;
|
||||
}
|
||||
return message.slice(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const prefix = message.slice(0, displaySubsystem.length);
|
||||
if (prefix.toLowerCase() !== displaySubsystem.toLowerCase()) return message;
|
||||
if (prefix.toLowerCase() !== displaySubsystem.toLowerCase()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const next = message.slice(displaySubsystem.length, displaySubsystem.length + 1);
|
||||
if (next !== ":" && next !== " ") return message;
|
||||
if (next !== ":" && next !== " ") {
|
||||
return message;
|
||||
}
|
||||
|
||||
let i = displaySubsystem.length;
|
||||
while (message[i] === " ") i += 1;
|
||||
if (message[i] === ":") i += 1;
|
||||
while (message[i] === " ") i += 1;
|
||||
while (message[i] === " ") {
|
||||
i += 1;
|
||||
}
|
||||
if (message[i] === ":") {
|
||||
i += 1;
|
||||
}
|
||||
while (message[i] === " ") {
|
||||
i += 1;
|
||||
}
|
||||
return message.slice(i);
|
||||
}
|
||||
|
||||
@@ -186,12 +210,16 @@ function logToFile(
|
||||
message: string,
|
||||
meta?: Record<string, unknown>,
|
||||
) {
|
||||
if (level === "silent") return;
|
||||
if (level === "silent") {
|
||||
return;
|
||||
}
|
||||
const safeLevel = level;
|
||||
const method = (fileLogger as unknown as Record<string, unknown>)[safeLevel] as
|
||||
| ((...args: unknown[]) => void)
|
||||
| undefined;
|
||||
if (typeof method !== "function") return;
|
||||
if (typeof method !== "function") {
|
||||
return;
|
||||
}
|
||||
if (meta && Object.keys(meta).length > 0) {
|
||||
method.call(fileLogger, meta, message);
|
||||
} else {
|
||||
@@ -202,7 +230,9 @@ function logToFile(
|
||||
export function createSubsystemLogger(subsystem: string): SubsystemLogger {
|
||||
let fileLogger: TsLogger<LogObj> | null = null;
|
||||
const getFileLogger = () => {
|
||||
if (!fileLogger) fileLogger = getChildLogger({ subsystem });
|
||||
if (!fileLogger) {
|
||||
fileLogger = getChildLogger({ subsystem });
|
||||
}
|
||||
return fileLogger;
|
||||
};
|
||||
const emit = (level: LogLevel, message: string, meta?: Record<string, unknown>) => {
|
||||
@@ -219,8 +249,12 @@ export function createSubsystemLogger(subsystem: string): SubsystemLogger {
|
||||
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
|
||||
}
|
||||
logToFile(getFileLogger(), level, message, fileMeta);
|
||||
if (!shouldLogToConsole(level, { level: consoleSettings.level })) return;
|
||||
if (!shouldLogSubsystemToConsole(subsystem)) return;
|
||||
if (!shouldLogToConsole(level, { level: consoleSettings.level })) {
|
||||
return;
|
||||
}
|
||||
if (!shouldLogSubsystemToConsole(subsystem)) {
|
||||
return;
|
||||
}
|
||||
const consoleMessage = consoleMessageOverride ?? message;
|
||||
if (
|
||||
!isVerbose() &&
|
||||
|
||||
Reference in New Issue
Block a user