context.js
1.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { randomUUID } from 'node:crypto'
/**
* @typedef {Object} RequestContext
* @property {string} traceId 贯穿一次请求的 trace id
* @property {Object} fields 累积的结构化字段(如 userId/route)
* @property {(extraFields?: Object) => RequestContext} child
* 派生子上下文:继承 `traceId`,merge 字段,返回**新** ctx(不可变派生,不污染父)
*/
/**
* 创建请求上下文。入口处一次,或从入站 trace header 续传。
* 不传 `traceId` 时自动生成(`crypto.randomUUID`);传则续用(跨服务/入站续传)。
*
* @param {Object} [opts]
* @param {string} [opts.traceId] 续传已有 trace_id;省略则自动生成
* @param {Object} [opts.fields] 初始附加字段(如 route)
* @returns {RequestContext}
*/
export function createRequestContext(opts) {
const traceId = opts && opts.traceId ? opts.traceId : randomUUID()
const fields = opts && opts.fields ? { ...opts.fields } : {}
return makeContext(traceId, fields)
}
/**
* 内部工厂:把 traceId + 已固化的 fields 包成不可变 RequestContext。
* @param {string} traceId
* @param {Object} fields 调用方已复制好的字段对象,本函数不再外泄其可变引用
* @returns {RequestContext}
*/
function makeContext(traceId, fields) {
/** @type {RequestContext} */
const ctx = {
traceId,
fields,
child(extraFields) {
// 不可变派生:合并出全新字段对象,父 ctx.fields 不被触碰。
const merged = { ...fields, ...(extraFields || {}) }
return makeContext(traceId, merged)
},
}
// 冻结对外可见形态,防止下游意外篡改 traceId/child 造成 trace 漂移。
Object.freeze(ctx.fields)
return Object.freeze(ctx)
}