81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
// server/utils/logging.ts - 安全日志工具,避免输出密钥、cookie 和完整敏感内容
|
||
import { randomUUID } from "node:crypto";
|
||
|
||
import type { H3Event } from "h3";
|
||
|
||
/**
|
||
* 常见敏感片段匹配
|
||
*
|
||
* 这里不是完整 DLP,只处理最容易误打出来的 key、Bearer token 和 token=xxx
|
||
* 真实请求体、响应体本来就不应该进入日志
|
||
*/
|
||
const SENSITIVE_VALUE_RE =
|
||
/(sk-[A-Za-z0-9_-]+|Bearer\s+[A-Za-z0-9._-]+|token[=:]\s*[^,\s}]+)/giu;
|
||
|
||
/** 对错误摘要做最小脱敏和截断,避免日志过长或泄露密钥 */
|
||
const sanitizeLogText = (value: string) => {
|
||
return value.replace(SENSITIVE_VALUE_RE, "[redacted]").slice(0, 500);
|
||
};
|
||
|
||
/**
|
||
* 将 unknown 异常变成安全可打印对象
|
||
*
|
||
* API 对前端统一返回“服务器内部错误”,日志里只保留类型和脱敏摘要,
|
||
* 既能排查方向,又不把上游响应体或 key 打出去
|
||
*/
|
||
export const toSafeLogError = (error: unknown) => {
|
||
if (error instanceof Error) {
|
||
return {
|
||
name: error.name,
|
||
message: sanitizeLogText(error.message)
|
||
};
|
||
}
|
||
|
||
return {
|
||
name: "UnknownError",
|
||
message: sanitizeLogText(String(error))
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 创建带 requestId 的 API 日志器
|
||
*
|
||
* 每个 handler 只需要记录“阶段”和少量结构化字段;这里统一补 method、path、
|
||
* 耗时等上下文,方便后续按一次请求串起来查
|
||
*/
|
||
export const createApiLogger = (event: H3Event, scope: string) => {
|
||
const requestId =
|
||
event.node.req.headers["x-request-id"]?.toString() || randomUUID();
|
||
const startedAt = Date.now();
|
||
|
||
/** 内部统一出口,避免不同 level 打出的字段不一致 */
|
||
const write = (
|
||
level: "info" | "warn" | "error",
|
||
stage: string,
|
||
extra: Record<string, unknown> = {}
|
||
) => {
|
||
const payload = {
|
||
requestId,
|
||
scope,
|
||
stage,
|
||
method: event.node.req.method,
|
||
path: event.path,
|
||
elapsedMs: Date.now() - startedAt,
|
||
...extra
|
||
};
|
||
|
||
console[level](`[${scope}] ${stage}`, JSON.stringify(payload));
|
||
};
|
||
|
||
/** 返回轻量 API,handler 不直接接触 console,便于以后替换日志实现 */
|
||
return {
|
||
requestId,
|
||
info: (stage: string, extra?: Record<string, unknown>) =>
|
||
write("info", stage, extra),
|
||
warn: (stage: string, extra?: Record<string, unknown>) =>
|
||
write("warn", stage, extra),
|
||
error: (stage: string, extra?: Record<string, unknown>) =>
|
||
write("error", stage, extra)
|
||
};
|
||
};
|