docs: plan explicit logger env migration
This commit is contained in:
871
docs/superpowers/plans/2026-06-17-explicit-env-logger.md
Normal file
871
docs/superpowers/plans/2026-06-17-explicit-env-logger.md
Normal file
@@ -0,0 +1,871 @@
|
||||
# Logger Env 显式传递实现计划
|
||||
|
||||
> **给 agent 执行者:** 必选子技能:使用 `superpowers:subagent-driven-development`(推荐)或 `superpowers:executing-plans` 按任务逐项实现本计划。步骤统一使用 checkbox(`- [ ]`)语法跟踪。
|
||||
|
||||
**目标:** 移除 `server/src` 中 logger 对空对象 `Env` 和模块级全局 `logger` 的依赖,强制 logger 初始化从入口真实 `env` 显式传递。
|
||||
|
||||
**方案概览:** 先扩展单元扫描测试,让默认 `Env`、`initLogger({}, {})`、`export const logger` 和从本项目 `logtailUtils` 导入全局 `logger` 都变成可见失败。再收紧 `initLogger` / `logtailUtils` 签名,删除 import-time logger,并按已知调用点把 `env` 或 `ctx.logger` 逐层传递。最后运行类型检查、单元测试和静态搜索,确认没有回归。
|
||||
|
||||
**技术栈:** TypeScript、Bun test、Hono、pino、Cloudflare Workers bindings、tsgo
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
- 修改:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
职责:测试 `initLogger` 使用显式 `Env`,并扫描 `server/src` 禁止默认 env 和全局 logger 模式。
|
||||
- 修改:`server/src/utils/logging/initLogger.ts`
|
||||
职责:将 `env` 参数从 `Partial<Env>` 收紧为 `Env`。
|
||||
- 修改:`server/src/external/logtail/logtailUtils.ts`
|
||||
职责:删除模块级 logger,只保留 `createLogger(env)`、`createDualLogger(env)` 和 `Logger` 类型。
|
||||
- 修改:`server/src/external/aws/eventbridge/eventBridgeUtils.ts`
|
||||
职责:用函数参数 `env: Env` 创建局部 logger,不导入全局 `logger`。
|
||||
- 修改:`server/src/internal/balances/utils/lock/cancelLockExpiry.ts`
|
||||
职责:把 Worker bindings `env: Env` 显式传给 `deleteSchedule`,业务环境重命名为 `appEnv`。
|
||||
- 修改:`server/src/internal/balances/finalizeLock/runFinalizeLock.ts`
|
||||
职责:调用 `cancelLockExpiry` 时传入 `workerEnv: ctx.bindings` 或等价显式 Worker `Env`。如果当前 `RequestContext` 没有 bindings,按本任务步骤新增窄字段。
|
||||
- 修改:`server/src/internal/balances/finalizeLock/runFinalizeLockV2.ts`
|
||||
职责:调用 `cancelLockExpiry` 时传入显式 Worker `Env` 和业务 `AppEnv`,保持 V2 finalize lock 路径与 V1 一致。
|
||||
- 修改:`server/src/honoUtils/HonoEnv.ts`
|
||||
职责:如果 finalize lock 调用链暂时没有 Worker `Env`,为 `RequestContext` 增加明确字段 `bindings: Env`,只用于 Worker bindings,不替代业务 `ctx.env: AppEnv`。
|
||||
- 修改:`server/src/honoMiddlewares/baseMiddleware.ts`
|
||||
职责:如果新增 `RequestContext.bindings`,在 Hono 边界把 `c.env` 写入上下文。
|
||||
- 修改:`server/src/queue/processMessage.ts`
|
||||
职责:`processMessage` 接收 `env: Env`,局部创建 logger,并用 `env.NODE_ENV` 替代 `runtimeEnv.NODE_ENV`。
|
||||
- 修改:`server/src/queue/initWorkers.ts`
|
||||
职责:调用 `processMessage` 时传入入口 env。
|
||||
- 修改:`server/src/internal/customers/cusUtils/fullCustomerCacheUtils/testFullCustomerCacheGuard.ts`
|
||||
职责:用 `ctx.logger` 记录错误,删除全局 `logger` 导入。
|
||||
- 检查并按需修改:`server/src/external/aws/eventbridge/initEventBridge.ts`、`server/src/queue/initSqs.ts`
|
||||
职责:确保 EventBridge/SQS client 初始化没有新增空 env fallback。
|
||||
|
||||
---
|
||||
|
||||
### 任务 1:扩展扫描测试,先暴露 logger env 违规
|
||||
|
||||
**文件:**
|
||||
- 修改:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
|
||||
- [ ] **步骤 1:替换测试文件内容**
|
||||
|
||||
将 `server/tests/unit/logging/init-logger-env.test.ts` 改为:
|
||||
|
||||
```ts
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { join, relative } from "node:path";
|
||||
import { initLogger } from "@/utils/logging/initLogger.js";
|
||||
|
||||
const makeEnv = (overrides: Partial<Env>): Env => overrides as Env;
|
||||
|
||||
const sourceRoot = join(process.cwd(), "src");
|
||||
|
||||
const forbiddenSourcePatterns = [
|
||||
{
|
||||
name: "Env parameter default",
|
||||
pattern: /\benv\s*:\s*Env\s*=/,
|
||||
},
|
||||
{
|
||||
name: "optional Env parameter",
|
||||
pattern: /\benv\?\s*:\s*Env\b/,
|
||||
},
|
||||
{
|
||||
name: "empty Env parameter fallback",
|
||||
pattern: /\benv\s*=\s*\{\}\s+as\s+Env\b/,
|
||||
},
|
||||
{
|
||||
name: "empty Env nullish fallback",
|
||||
pattern: /\benv\s*\?\?\s*\(?\s*\{\}\s+as\s+Env\b/,
|
||||
},
|
||||
{
|
||||
name: "initLogger empty Env",
|
||||
pattern: /\binitLogger\(\s*\{\s*\}\s*,\s*\{\s*\}\s*\)/,
|
||||
},
|
||||
{
|
||||
name: "createLoggerStructure empty Env",
|
||||
pattern: /\bcreateLoggerStructure\([^;]*,\s*\{\s*\}\s*\)/,
|
||||
},
|
||||
{
|
||||
name: "global logtail logger export",
|
||||
pattern: /\bexport\s+const\s+logger\b/,
|
||||
},
|
||||
{
|
||||
name: "global logtail logger import",
|
||||
pattern:
|
||||
/import\s+\{[^}]*\blogger\b[^}]*\}\s+from\s+["'](?:@\/external\/logtail\/logtailUtils(?:\.js)?|[^"']*external\/logtail\/logtailUtils(?:\.js)?)["']/,
|
||||
},
|
||||
{
|
||||
name: "logger Env typed as Partial",
|
||||
pattern: /\benv\s*:\s*Partial<Env>\b/,
|
||||
files: new Set([
|
||||
"src/utils/logging/initLogger.ts",
|
||||
"src/external/logtail/logtailUtils.ts",
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
const collectTypeScriptFiles = (dir: string): string[] =>
|
||||
readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
||||
const path = join(dir, entry.name);
|
||||
if (entry.isDirectory()) return collectTypeScriptFiles(path);
|
||||
if (!entry.isFile()) return [];
|
||||
return /\.(ts|tsx)$/.test(entry.name) ? [path] : [];
|
||||
});
|
||||
|
||||
const findForbiddenSourcePatterns = () =>
|
||||
collectTypeScriptFiles(sourceRoot).flatMap((file) => {
|
||||
const relativePath = relative(process.cwd(), file);
|
||||
const contents = readFileSync(file, "utf8");
|
||||
|
||||
return forbiddenSourcePatterns
|
||||
.filter((rule) => !rule.files || rule.files.has(relativePath))
|
||||
.filter((rule) => rule.pattern.test(contents))
|
||||
.map((rule) => `${relativePath}: ${rule.name}`);
|
||||
});
|
||||
|
||||
describe("initLogger env configuration", () => {
|
||||
const originalNodeEnv = process.env.NODE_ENV;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalNodeEnv === undefined) {
|
||||
delete (process.env as Record<string, string | undefined>).NODE_ENV;
|
||||
} else {
|
||||
process.env.NODE_ENV = originalNodeEnv;
|
||||
}
|
||||
});
|
||||
|
||||
test("uses the passed Env instead of process.env to resolve dev mode", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
const logger = initLogger(
|
||||
{},
|
||||
makeEnv({
|
||||
NODE_ENV: "development",
|
||||
AXIOM_TOKEN: "",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(logger.level).toBe("debug");
|
||||
});
|
||||
|
||||
test("does not allow default Env or global logtail logger usage", () => {
|
||||
expect(findForbiddenSourcePatterns()).toEqual([]);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **步骤 2:运行测试,确认它先失败**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:FAIL。失败列表至少包含:
|
||||
|
||||
```text
|
||||
src/external/logtail/logtailUtils.ts: initLogger empty Env
|
||||
src/external/logtail/logtailUtils.ts: createLoggerStructure empty Env
|
||||
src/external/logtail/logtailUtils.ts: global logtail logger export
|
||||
src/external/logtail/logtailUtils.ts: logger Env typed as Partial
|
||||
```
|
||||
|
||||
如果同时列出 `src/queue/processMessage.ts`、`src/external/aws/eventbridge/eventBridgeUtils.ts` 或 `src/internal/customers/cusUtils/fullCustomerCacheUtils/testFullCustomerCacheGuard.ts` 的全局 logger import,这是符合预期的。
|
||||
|
||||
- [ ] **步骤 3:提交失败测试**
|
||||
|
||||
```bash
|
||||
git add server/tests/unit/logging/init-logger-env.test.ts
|
||||
git commit -m "test: forbid implicit logger env"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 任务 2:收紧 logger factory,删除模块级 logger
|
||||
|
||||
**文件:**
|
||||
- 修改:`server/src/utils/logging/initLogger.ts`
|
||||
- 修改:`server/src/external/logtail/logtailUtils.ts`
|
||||
- 测试:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
|
||||
- [ ] **步骤 1:收紧 `initLogger` 签名**
|
||||
|
||||
在 `server/src/utils/logging/initLogger.ts` 中,把函数签名从:
|
||||
|
||||
```ts
|
||||
export const initLogger = (
|
||||
options: InitLoggerOptions,
|
||||
env: Partial<Env>,
|
||||
) => {
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
export const initLogger = (options: InitLoggerOptions, env: Env) => {
|
||||
```
|
||||
|
||||
函数体不需要改变,继续读取 `env.NODE_ENV` 和 `env.AXIOM_TOKEN`。
|
||||
|
||||
- [ ] **步骤 2:删除 `logtailUtils.ts` 的 import-time logger**
|
||||
|
||||
在 `server/src/external/logtail/logtailUtils.ts` 中删除这两段:
|
||||
|
||||
```ts
|
||||
const pinoLogger = initLogger({}, {});
|
||||
```
|
||||
|
||||
```ts
|
||||
export const logger = createLoggerStructure(pinoLogger, {});
|
||||
```
|
||||
|
||||
- [ ] **步骤 3:收紧 `createLoggerStructure` 的 env 类型**
|
||||
|
||||
在 `server/src/external/logtail/logtailUtils.ts` 中,把:
|
||||
|
||||
```ts
|
||||
const createLoggerStructure = (
|
||||
basePinoLogger: pino.Logger,
|
||||
env: Partial<Env>,
|
||||
): Logger => ({
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
const createLoggerStructure = (
|
||||
basePinoLogger: pino.Logger,
|
||||
env: Env,
|
||||
): Logger => ({
|
||||
```
|
||||
|
||||
- [ ] **步骤 4:确认 factory 出口仍然显式接收 Env**
|
||||
|
||||
确认 `server/src/external/logtail/logtailUtils.ts` 中保留以下代码:
|
||||
|
||||
```ts
|
||||
export const createLogger = (env: Env) =>
|
||||
createLoggerStructure(initLogger({}, env), env);
|
||||
|
||||
/**
|
||||
* Lazy dual-output logger (stdout JSON + axiom). Used only by long-running
|
||||
* trigger.dev tasks so their lines surface in both the trigger run UI and
|
||||
* our axiom store. Default `logger` / `createLogger` are unaffected.
|
||||
*/
|
||||
export const createDualLogger = (env: Env) =>
|
||||
createLoggerStructure(initLogger({ mode: "dual" }, env), env);
|
||||
```
|
||||
|
||||
如果注释中的 `Default logger` 文案不再准确,把注释中的这一句改为:
|
||||
|
||||
```ts
|
||||
* `createLogger` is unaffected.
|
||||
```
|
||||
|
||||
- [ ] **步骤 5:运行测试,确认剩余失败只来自调用点**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:仍 FAIL,但不再包含:
|
||||
|
||||
```text
|
||||
src/external/logtail/logtailUtils.ts: initLogger empty Env
|
||||
src/external/logtail/logtailUtils.ts: createLoggerStructure empty Env
|
||||
src/external/logtail/logtailUtils.ts: global logtail logger export
|
||||
src/external/logtail/logtailUtils.ts: logger Env typed as Partial
|
||||
```
|
||||
|
||||
如果仍失败,应只剩其他文件从 `logtailUtils` 导入全局 `logger`。
|
||||
|
||||
- [ ] **步骤 6:提交 logger factory 改动**
|
||||
|
||||
```bash
|
||||
git add server/src/utils/logging/initLogger.ts server/src/external/logtail/logtailUtils.ts
|
||||
git commit -m "refactor: require env for logger factories"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 任务 3:迁移 EventBridge logger,并补齐取消 schedule 的 Worker Env
|
||||
|
||||
**文件:**
|
||||
- 修改:`server/src/external/aws/eventbridge/eventBridgeUtils.ts`
|
||||
- 修改:`server/src/internal/balances/utils/lock/cancelLockExpiry.ts`
|
||||
- 修改:`server/src/internal/balances/finalizeLock/runFinalizeLock.ts`
|
||||
- 修改:`server/src/internal/balances/finalizeLock/runFinalizeLockV2.ts`
|
||||
- 按需修改:`server/src/honoUtils/HonoEnv.ts`
|
||||
- 按需修改:`server/src/honoMiddlewares/baseMiddleware.ts`
|
||||
- 测试:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
|
||||
- [ ] **步骤 1:把 EventBridge 全局 logger 改成 factory**
|
||||
|
||||
在 `server/src/external/aws/eventbridge/eventBridgeUtils.ts` 中,把导入:
|
||||
|
||||
```ts
|
||||
import { logger } from "@/external/logtail/logtailUtils.js";
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
import { createLogger } from "@/external/logtail/logtailUtils.js";
|
||||
```
|
||||
|
||||
在 `createSchedule` 函数体开始处加入:
|
||||
|
||||
```ts
|
||||
const logger = createLogger(env);
|
||||
```
|
||||
|
||||
完整目标片段:
|
||||
|
||||
```ts
|
||||
export const createSchedule = async ({
|
||||
env,
|
||||
scheduleName,
|
||||
scheduleAt,
|
||||
sqsMessageBody,
|
||||
messageGroupId,
|
||||
}: {
|
||||
env: Env;
|
||||
scheduleName: string;
|
||||
scheduleAt: Date;
|
||||
sqsMessageBody: string;
|
||||
messageGroupId: string;
|
||||
}) => {
|
||||
const logger = createLogger(env);
|
||||
|
||||
if (isLocalQueue(env)) {
|
||||
logger.debug(
|
||||
"[EventBridge] createSchedule skipped (local SQS queue — no EventBridge in dev)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
在 `deleteSchedule` 函数体开始处加入:
|
||||
|
||||
```ts
|
||||
const logger = createLogger(env);
|
||||
```
|
||||
|
||||
完整目标片段:
|
||||
|
||||
```ts
|
||||
export const deleteSchedule = async ({
|
||||
env,
|
||||
scheduleName,
|
||||
}: {
|
||||
env: Env;
|
||||
scheduleName: string;
|
||||
}) => {
|
||||
const logger = createLogger(env);
|
||||
|
||||
if (isLocalQueue(env)) {
|
||||
logger.debug(
|
||||
"[EventBridge] deleteSchedule skipped (local SQS queue — no EventBridge in dev)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **步骤 2:让 `cancelLockExpiry` 同时接收 Worker Env 和业务 AppEnv**
|
||||
|
||||
把 `server/src/internal/balances/utils/lock/cancelLockExpiry.ts` 改为:
|
||||
|
||||
```ts
|
||||
import { deleteSchedule } from "@/external/aws/eventbridge/eventBridgeUtils.js";
|
||||
import { buildLockScheduleName } from "./buildLockScheduleName.js";
|
||||
|
||||
/** Cancels the EventBridge expiry schedule for a lock receipt. Safe to call even if no schedule exists. */
|
||||
export const cancelLockExpiry = async ({
|
||||
workerEnv,
|
||||
orgId,
|
||||
appEnv,
|
||||
hashedKey,
|
||||
}: {
|
||||
workerEnv: Env;
|
||||
orgId: string;
|
||||
appEnv: string;
|
||||
hashedKey: string;
|
||||
}) => {
|
||||
const scheduleName = buildLockScheduleName({
|
||||
orgId,
|
||||
env: appEnv,
|
||||
hashedKey,
|
||||
});
|
||||
await deleteSchedule({ env: workerEnv, scheduleName });
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **步骤 3:如果 `RequestContext` 还没有 Worker bindings,新增窄字段**
|
||||
|
||||
检查 `server/src/honoUtils/HonoEnv.ts`。如果 `RequestContext` 中没有 Worker bindings 字段,在 `logger: Logger;` 后加入:
|
||||
|
||||
```ts
|
||||
bindings: Env;
|
||||
```
|
||||
|
||||
目标片段:
|
||||
|
||||
```ts
|
||||
db: DrizzleCli;
|
||||
dbGeneral: DrizzleCli;
|
||||
logger: Logger;
|
||||
bindings: Env;
|
||||
/** V2 Redis instance for this request. Populated by every ctx-building
|
||||
```
|
||||
|
||||
然后在 `server/src/honoMiddlewares/baseMiddleware.ts` 的 `c.set("ctx", { ... })` 对象里,紧跟 `logger,` 加入:
|
||||
|
||||
```ts
|
||||
bindings: c.env,
|
||||
```
|
||||
|
||||
目标片段:
|
||||
|
||||
```ts
|
||||
db,
|
||||
dbGeneral: db,
|
||||
logger,
|
||||
bindings: c.env,
|
||||
redisV2,
|
||||
```
|
||||
|
||||
如果已有等价字段,例如 `workerEnv: Env`,不要新增第二个字段;后续步骤统一使用已有字段名。
|
||||
|
||||
- [ ] **步骤 4:更新 finalize lock 调用点**
|
||||
|
||||
在 `server/src/internal/balances/finalizeLock/runFinalizeLock.ts` 中,把:
|
||||
|
||||
```ts
|
||||
await cancelLockExpiry({
|
||||
orgId: ctx.org.id,
|
||||
env: ctx.env,
|
||||
hashedKey: Bun.hash(params.lock_id).toString(),
|
||||
});
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
await cancelLockExpiry({
|
||||
workerEnv: ctx.bindings,
|
||||
orgId: ctx.org.id,
|
||||
appEnv: ctx.env,
|
||||
hashedKey: Bun.hash(params.lock_id).toString(),
|
||||
});
|
||||
```
|
||||
|
||||
在 `server/src/internal/balances/finalizeLock/runFinalizeLockV2.ts` 中做同样替换。
|
||||
|
||||
- [ ] **步骤 5:运行局部静态检查**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
rg -n "import \\{[^}]*logger[^}]*\\} from [\"']@/external/logtail/logtailUtils|deleteSchedule\\(\\{ scheduleName|cancelLockExpiry\\(\\{" server/src/external/aws/eventbridge server/src/internal/balances
|
||||
```
|
||||
|
||||
预期:不再有 `logger` 从本项目 `logtailUtils` 导入;不再有 `deleteSchedule({ scheduleName })`。`cancelLockExpiry({` 的调用点必须都包含 `workerEnv` 和 `appEnv`。
|
||||
|
||||
- [ ] **步骤 6:运行 logger 扫描测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:如果还有失败,应来自其他全局 logger 调用点,不来自 `eventBridgeUtils.ts`。
|
||||
|
||||
- [ ] **步骤 7:提交 EventBridge 改动**
|
||||
|
||||
```bash
|
||||
git add server/src/external/aws/eventbridge/eventBridgeUtils.ts server/src/internal/balances/utils/lock/cancelLockExpiry.ts server/src/internal/balances/finalizeLock/runFinalizeLock.ts server/src/internal/balances/finalizeLock/runFinalizeLockV2.ts server/src/honoUtils/HonoEnv.ts server/src/honoMiddlewares/baseMiddleware.ts
|
||||
git commit -m "refactor: pass env into eventbridge logging"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 任务 4:迁移 SQS message 处理 logger 和 runtimeEnv 读取
|
||||
|
||||
**文件:**
|
||||
- 修改:`server/src/queue/processMessage.ts`
|
||||
- 修改:`server/src/queue/initWorkers.ts`
|
||||
- 测试:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
- 测试:`server/tests/unit/runtime-env-imports.test.ts`
|
||||
|
||||
- [ ] **步骤 1:替换 `processMessage.ts` 的导入**
|
||||
|
||||
在 `server/src/queue/processMessage.ts` 中删除:
|
||||
|
||||
```ts
|
||||
import { runtimeEnv } from "@/utils/envUtils.js";
|
||||
```
|
||||
|
||||
把:
|
||||
|
||||
```ts
|
||||
import { logger } from "@/external/logtail/logtailUtils.js";
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
import { createLogger } from "@/external/logtail/logtailUtils.js";
|
||||
```
|
||||
|
||||
- [ ] **步骤 2:让 `processMessage` 显式接收 Worker Env**
|
||||
|
||||
把函数签名从:
|
||||
|
||||
```ts
|
||||
export const processMessage = async ({
|
||||
message,
|
||||
db,
|
||||
}: {
|
||||
message: Message;
|
||||
db: DrizzleCli;
|
||||
}) => {
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
export const processMessage = async ({
|
||||
env,
|
||||
message,
|
||||
db,
|
||||
}: {
|
||||
env: Env;
|
||||
message: Message;
|
||||
db: DrizzleCli;
|
||||
}) => {
|
||||
```
|
||||
|
||||
- [ ] **步骤 3:用入口 env 创建 worker logger**
|
||||
|
||||
把:
|
||||
|
||||
```ts
|
||||
const workerLogger = addWorkflowToLogs({
|
||||
logger: logger,
|
||||
workflowContext: {
|
||||
id: workflowId,
|
||||
name: job.name,
|
||||
payload: job.data,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
const workerLogger = addWorkflowToLogs({
|
||||
logger: createLogger(env),
|
||||
workflowContext: {
|
||||
id: workflowId,
|
||||
name: job.name,
|
||||
payload: job.data,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **步骤 4:用显式 env 替代 runtimeEnv**
|
||||
|
||||
把:
|
||||
|
||||
```ts
|
||||
if (runtimeEnv.NODE_ENV === "development") {
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
if (env.NODE_ENV === "development") {
|
||||
```
|
||||
|
||||
- [ ] **步骤 5:更新 `initWorkers.ts` 调用点**
|
||||
|
||||
在 `server/src/queue/initWorkers.ts` 中,把两个调用点:
|
||||
|
||||
```ts
|
||||
await processMessage({ message, db });
|
||||
```
|
||||
|
||||
和:
|
||||
|
||||
```ts
|
||||
fn: () => processMessage({ message, db }),
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
await processMessage({ env, message, db });
|
||||
```
|
||||
|
||||
和:
|
||||
|
||||
```ts
|
||||
fn: () => processMessage({ env, message, db }),
|
||||
```
|
||||
|
||||
这里的 `env` 必须来自 `initWorkers(env: Env)` 或等价入口参数;不要从模块级变量读取。
|
||||
|
||||
- [ ] **步骤 6:运行局部静态检查**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
rg -n "runtimeEnv|import \\{[^}]*logger[^}]*\\} from [\"']@/external/logtail/logtailUtils|processMessage\\(\\{ message, db \\}\\)" server/src/queue
|
||||
```
|
||||
|
||||
预期:无输出。
|
||||
|
||||
- [ ] **步骤 7:运行相关扫描测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/runtime-env-imports.test.ts tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:如果仍失败,不应再包含 `src/queue/processMessage.ts`。
|
||||
|
||||
- [ ] **步骤 8:提交队列改动**
|
||||
|
||||
```bash
|
||||
git add server/src/queue/processMessage.ts server/src/queue/initWorkers.ts
|
||||
git commit -m "refactor: pass env into sqs message logging"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 任务 5:迁移 cache guard 的全局 logger
|
||||
|
||||
**文件:**
|
||||
- 修改:`server/src/internal/customers/cusUtils/fullCustomerCacheUtils/testFullCustomerCacheGuard.ts`
|
||||
- 测试:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
|
||||
- [ ] **步骤 1:删除全局 logger 导入**
|
||||
|
||||
在 `server/src/internal/customers/cusUtils/fullCustomerCacheUtils/testFullCustomerCacheGuard.ts` 中删除:
|
||||
|
||||
```ts
|
||||
import { logger } from "@/external/logtail/logtailUtils.js";
|
||||
```
|
||||
|
||||
- [ ] **步骤 2:使用 `ctx.logger` 记录 set 失败**
|
||||
|
||||
把:
|
||||
|
||||
```ts
|
||||
logger.error(`Failed to set test fullCustomer cache guard: ${error}`);
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
ctx.logger.error(`Failed to set test fullCustomer cache guard: ${error}`);
|
||||
```
|
||||
|
||||
- [ ] **步骤 3:使用 `ctx.logger` 记录 remove 失败**
|
||||
|
||||
把:
|
||||
|
||||
```ts
|
||||
logger.error(`Failed to remove test fullCustomer cache guard: ${error}`);
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```ts
|
||||
ctx.logger.error(`Failed to remove test fullCustomer cache guard: ${error}`);
|
||||
```
|
||||
|
||||
- [ ] **步骤 4:运行 logger 扫描测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:如果任务 1-5 全部完成,测试 PASS,或者失败列表指向新的全局 logger import 文件。
|
||||
|
||||
- [ ] **步骤 5:如果扫描发现新的全局 logger import,逐个迁移**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
rg -n "import \\{[^}]*logger[^}]*\\} from [\"'](?:@/external/logtail/logtailUtils(?:\\.js)?|[^\"']*external/logtail/logtailUtils(?:\\.js)?)[\"']" server/src
|
||||
```
|
||||
|
||||
对每个命中文件使用同一规则迁移:
|
||||
|
||||
```ts
|
||||
// 如果函数已有 ctx
|
||||
ctx.logger.error("message");
|
||||
|
||||
// 如果函数已有 env: Env
|
||||
const logger = createLogger(env);
|
||||
logger.error("message");
|
||||
```
|
||||
|
||||
迁移后再次运行同一个 `rg` 命令,预期无输出。不要修改 `better-auth` 的 `logger` 导入。
|
||||
|
||||
- [ ] **步骤 6:提交 cache guard 改动**
|
||||
|
||||
```bash
|
||||
git add server/src/internal/customers/cusUtils/fullCustomerCacheUtils/testFullCustomerCacheGuard.ts
|
||||
git commit -m "refactor: use context logger for cache guard"
|
||||
```
|
||||
|
||||
如果步骤 5 迁移了额外文件,把这些文件加入同一个提交,并在提交信息中使用:
|
||||
|
||||
```bash
|
||||
git commit -m "refactor: remove global logtail logger imports"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 任务 6:全量静态搜索和类型检查修复
|
||||
|
||||
**文件:**
|
||||
- 按类型检查输出修改相关 `server/src/**` 文件
|
||||
- 测试:`server/tests/unit/logging/init-logger-env.test.ts`
|
||||
- 测试:`server/tests/unit/runtime-env-imports.test.ts`
|
||||
|
||||
- [ ] **步骤 1:运行最终静态搜索**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
rg -n "initLogger\\(\\s*\\{\\s*\\}\\s*,\\s*\\{\\s*\\}\\s*\\)|createLoggerStructure\\([^;]*,\\s*\\{\\s*\\}\\s*\\)|export const logger\\b|import \\{[^}]*logger[^}]*\\} from [\"'](?:@/external/logtail/logtailUtils(?:\\.js)?|[^\"']*external/logtail/logtailUtils(?:\\.js)?)[\"']|env\\?\\s*:\\s*Env\\b|env\\s*:\\s*Env\\s*=|env\\s*=\\s*\\{\\}\\s+as\\s+Env" server/src
|
||||
```
|
||||
|
||||
预期:无输出。
|
||||
|
||||
- [ ] **步骤 2:运行 runtime env 和 logger 单元扫描测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test --isolate tests/unit/runtime-env-imports.test.ts tests/unit/logging/init-logger-env.test.ts
|
||||
```
|
||||
|
||||
预期:PASS。
|
||||
|
||||
- [ ] **步骤 3:运行类型检查**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun ts
|
||||
```
|
||||
|
||||
预期:PASS。
|
||||
|
||||
如果失败,按错误文件修复。常见修复:
|
||||
|
||||
```ts
|
||||
// 缺 env 参数
|
||||
await processMessage({ env, message, db });
|
||||
|
||||
// 业务 AppEnv 和 Worker Env 混用
|
||||
await cancelLockExpiry({
|
||||
workerEnv: ctx.bindings,
|
||||
orgId: ctx.org.id,
|
||||
appEnv: ctx.env,
|
||||
hashedKey,
|
||||
});
|
||||
|
||||
// logger singleton 删除后改用上下文 logger
|
||||
ctx.logger.warn("message");
|
||||
```
|
||||
|
||||
每次修复后重新运行:
|
||||
|
||||
```bash
|
||||
cd server && bun ts
|
||||
```
|
||||
|
||||
直到类型检查 PASS。
|
||||
|
||||
- [ ] **步骤 4:提交类型修复**
|
||||
|
||||
如果步骤 3 修改了文件:
|
||||
|
||||
```bash
|
||||
git add server/src server/tests/unit/logging/init-logger-env.test.ts
|
||||
git commit -m "fix: thread env through logger call sites"
|
||||
```
|
||||
|
||||
如果步骤 3 没有源码改动,不创建提交。
|
||||
|
||||
---
|
||||
|
||||
### 任务 7:运行单元测试并提交最终验证状态
|
||||
|
||||
**文件:**
|
||||
- 检查:`server/src/**`
|
||||
- 检查:`server/tests/unit/**`
|
||||
|
||||
- [ ] **步骤 1:运行完整单元测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd server && bun test:unit
|
||||
```
|
||||
|
||||
预期:PASS。
|
||||
|
||||
如果失败且是本次 logger/env 改动引起,修复后重跑:
|
||||
|
||||
```bash
|
||||
cd server && bun test:unit
|
||||
```
|
||||
|
||||
如果失败来自外部服务、缺少本地环境或已存在不相关失败,记录失败命令和首个错误摘要,不把结果报告为通过。
|
||||
|
||||
- [ ] **步骤 2:检查工作区只包含本次相关改动**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
```
|
||||
|
||||
预期:至少本轮提交都已完成;如果仍有未提交改动,只能包含本计划相关文件。不要回滚用户已有改动。
|
||||
|
||||
- [ ] **步骤 3:如果还有本次相关未提交改动,提交它们**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
git add server/src server/tests/unit/logging/init-logger-env.test.ts
|
||||
git commit -m "test: verify explicit logger env"
|
||||
```
|
||||
|
||||
如果没有未提交的本次相关改动,不创建提交。
|
||||
|
||||
- [ ] **步骤 4:汇总验证结果**
|
||||
|
||||
最终回复中列出:
|
||||
|
||||
```text
|
||||
类型检查:cd server && bun ts -> PASS
|
||||
单元测试:cd server && bun test:unit -> PASS
|
||||
静态搜索:无隐式 logger env 或全局 logtail logger
|
||||
提交:列出本轮提交 hash 和 message
|
||||
```
|
||||
|
||||
如果任何命令未通过,使用实际结果替换 `PASS`,并说明第一个阻塞错误。
|
||||
Reference in New Issue
Block a user