Files
weixin-plugin-test/weixin-plugin/weixin/util/logger.ts
zyh 77019ec050 feat: 初始化微信插件独立测试环境
添加完整的微信插件测试框架,包含:
- 微信插件源码(weixin-plugin/)
- TypeScript 类型定义(types.ts)
- 插件编译脚本(scripts/build-weixin-plugin.sh)
- 简化测试脚本(scripts/test-weixin-simple.js)
- 完整测试脚本(scripts/test-weixin-plugin.js)
- 项目配置和依赖管理(package.json)
- 使用说明文档(README.md)

主要功能:
- 支持二维码登录认证
- HTTP 长轮询消息接收
- 文本、图片、视频、文件消息收发
- 自动回复测试
- 运行状态统计

Generated with [Claude Code](https://claude.com/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-03-30 07:14:00 +00:00

141 lines
3.8 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import * as https from "node:https";
import * as http from "node:http";
const RUNTIME = "node";
const RUNTIME_VERSION = process.versions.node;
const HOSTNAME = os.hostname() || "unknown";
function resolveMainLogDir(): string {
const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() ||
process.env.CLAWDBOT_STATE_DIR?.trim() ||
path.join(os.homedir(), ".openclaw");
return path.join(stateDir, "logs");
}
const MAIN_LOG_DIR = resolveMainLogDir();
const SUBSYSTEM = "originclawai/weixin";
const LEVEL_IDS: Record<string, number> = {
TRACE: 1,
DEBUG: 2,
INFO: 3,
WARN: 4,
ERROR: 5,
FATAL: 6,
};
const DEFAULT_LOG_LEVEL = "INFO";
function resolveMinLevel(): number {
const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase() ||
process.env.LOG_LEVEL?.toUpperCase();
if (env && env in LEVEL_IDS) return LEVEL_IDS[env];
return LEVEL_IDS[DEFAULT_LOG_LEVEL];
}
let minLevelId = resolveMinLevel();
export function setLogLevel(level: string): void {
const upper = level.toUpperCase();
if (!(upper in LEVEL_IDS)) {
throw new Error(`Invalid log level: ${level}. Valid levels: ${Object.keys(LEVEL_IDS).join(", ")}`);
}
minLevelId = LEVEL_IDS[upper];
}
function toLocalISO(now: Date): string {
const offsetMs = -now.getTimezoneOffset() * 60_000;
const sign = offsetMs >= 0 ? "+" : "-";
const abs = Math.abs(now.getTimezoneOffset());
const offStr = `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
return new Date(now.getTime() + offsetMs).toISOString().replace("Z", offStr);
}
function localDateKey(now: Date): string {
return toLocalISO(now).slice(0, 10);
}
function resolveMainLogPath(): string {
const dateKey = localDateKey(new Date());
return path.join(MAIN_LOG_DIR, `originclawai-${dateKey}.log`);
}
let logDirEnsured = false;
export type Logger = {
info(message: string): void;
debug(message: string): void;
warn(message: string): void;
error(message: string): void;
withAccount(accountId: string): Logger;
getLogFilePath(): string;
close(): void;
};
function buildLoggerName(accountId?: string): string {
return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
}
function writeLog(level: string, message: string, accountId?: string): void {
const levelId = LEVEL_IDS[level] ?? LEVEL_IDS.INFO;
if (levelId < minLevelId) return;
const now = new Date();
const loggerName = buildLoggerName(accountId);
const prefixedMessage = accountId ? `[${accountId}] ${message}` : message;
const entry = JSON.stringify({
"0": loggerName,
"1": prefixedMessage,
_meta: {
runtime: RUNTIME,
runtimeVersion: RUNTIME_VERSION,
hostname: HOSTNAME,
name: loggerName,
parentNames: ["originclawai"],
date: now.toISOString(),
logLevelId: LEVEL_IDS[level] ?? LEVEL_IDS.INFO,
logLevelName: level,
},
time: toLocalISO(now),
});
try {
if (!logDirEnsured) {
fs.mkdirSync(MAIN_LOG_DIR, { recursive: true });
logDirEnsured = true;
}
fs.appendFileSync(resolveMainLogPath(), `${entry}\n`, "utf-8");
} catch {
// Best-effort
}
}
function createLogger(accountId?: string): Logger {
return {
info(message: string): void {
writeLog("INFO", message, accountId);
},
debug(message: string): void {
writeLog("DEBUG", message, accountId);
},
warn(message: string): void {
writeLog("WARN", message, accountId);
},
error(message: string): void {
writeLog("ERROR", message, accountId);
},
withAccount(id: string): Logger {
return createLogger(id);
},
getLogFilePath(): string {
return resolveMainLogPath();
},
close(): void {
// No-op
},
};
}
export const logger: Logger = createLogger();