docs: plan explicit logger env migration

This commit is contained in:
2026-06-17 06:51:37 -07:00
parent 3f4c67c78e
commit 4488d2c6d4

View 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`,并说明第一个阻塞错误。