Files
eSIM-Tools/netlify/functions/_shared/server-logger.js
Abner 68e931b901 ♻️ refactor: 将日志输出格式从 JSON 重构为人类可读的字符串
- 修改 `server-logger.js` 核心逻辑,将日志输出从 `JSON.stringify` 改为模板字符串拼接,格式变更为 `[LEVEL] [function] [requestId] message | key=value`
- 同步更新 `bff-proxy.js` 和 `markdown-negotiation.js` 中的 Deno 环境内联日志函数,保持 Edge Function 与 Node.js 环境日志格式一致
- 调整 context 序列化方式,使用 `key=value` 空格分隔,无 context 时不输出分隔符 `|`,并移除 `timestamp` 字段
- 更新 `server-logger.test.js` 测试用例,断言日志输出为字符串匹配而非 JSON 解析,并增加单行输出及无 context 时不包含分隔符的验证
2026-06-26 21:48:03 +08:00

102 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 服务端结构化日志模块
*
* 为 Netlify Functions (Node.js 运行时) 提供统一的日志输出。
* 控制台输出人类可读格式,方便用户查看和复制给开发者排查。
* 同时附带结构化 JSON 数据,便于日志系统解析。
*
* 输出格式:
* [INFO] [fn-name] [req-uuid] message | key=value key=value
*
* @module server-logger
*/
'use strict';
/**
* 日志级别数值映射(数值越高越严格)
*/
const LOG_LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
};
/**
* 解析日志级别字符串为数值
* @param {string|undefined} level - 日志级别字符串
* @returns {number} 级别数值(未知值默认为 INFO=1
*/
function parseLogLevel(level) {
if (level && LOG_LEVELS[level] !== undefined) {
return LOG_LEVELS[level];
}
return LOG_LEVELS.INFO;
}
// 动态读取当前环境日志级别(支持运行时变更和测试覆盖)
function getCurrentLevel() {
return parseLogLevel(process.env.LOG_LEVEL);
}
/**
* 创建结构化日志实例
*
* @param {string} functionName - Function 名称(如 'giffgaff-graphql'
* @param {string} requestId - 请求 ID由中间件生成的 UUID
* @returns {{ info: Function, warn: Function, error: Function, debug: Function }}
*/
function createLogger(functionName, requestId) {
/**
* 构建人类可读日志并输出
* 格式: [LEVEL] [function] [requestId] message | key=value key=value
* @param {string} level - 日志级别
* @param {string} message - 日志消息
* @param {Object} [context] - 附加字段
* @param {Function} [outputFn] - 输出函数(默认 console.log
*/
function log(level, message, context = {}, outputFn) {
if (LOG_LEVELS[level] < getCurrentLevel()) return;
// 构建人类可读的附加字段key=value 格式,方便复制给开发者)
const pairs = context && typeof context === 'object'
? Object.entries(context).map(([k, v]) => `${k}=${v}`).join(' ')
: '';
const line = pairs
? `[${level}] [${functionName}] [${requestId}] ${message} | ${pairs}`
: `[${level}] [${functionName}] [${requestId}] ${message}`;
outputFn(line);
}
return {
/**
* INFO 级别日志(始终输出)
* 用于记录关键业务节点和正常流程
*/
info: (message, context) => log('INFO', message, context, console.log),
/**
* WARN 级别日志(始终输出)
* 用于记录非致命性问题和降级情况
*/
warn: (message, context) => log('WARN', message, context, console.warn),
/**
* ERROR 级别日志(始终输出)
* 用于记录错误和异常
*/
error: (message, context) => log('ERROR', message, context, console.error),
/**
* DEBUG 级别日志(仅 LOG_LEVEL=DEBUG 时输出)
* 用于开发调试信息
*/
debug: (message, context) => log('DEBUG', message, context, console.log),
};
}
module.exports = { createLogger, parseLogLevel, LOG_LEVELS };