autotel-edge 3.16.13 → 3.16.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-0FktBGyj.js +290 -0
- package/dist/config-0FktBGyj.js.map +1 -0
- package/dist/events.d.ts +42 -43
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +144 -137
- package/dist/events.js.map +1 -1
- package/dist/execution-logger-BWlya70r.d.ts +137 -0
- package/dist/execution-logger-BWlya70r.d.ts.map +1 -0
- package/dist/execution-logger-CoxSsBmU.js +247 -0
- package/dist/execution-logger-CoxSsBmU.js.map +1 -0
- package/dist/index.d.ts +192 -276
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +930 -1060
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +122 -136
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +783 -774
- package/dist/logger.js.map +1 -1
- package/dist/parse-error.d.ts +12 -10
- package/dist/parse-error.d.ts.map +1 -0
- package/dist/parse-error.js +55 -2
- package/dist/parse-error.js.map +1 -1
- package/dist/sampling.d.ts +51 -76
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +155 -90
- package/dist/sampling.js.map +1 -1
- package/dist/testing.d.ts +1 -2
- package/dist/testing.js +1 -3
- package/dist/types-HCbsjZzp.d.ts +214 -0
- package/dist/types-HCbsjZzp.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/core/buffer.ts +4 -3
- package/src/core/provider.context.test.ts +55 -0
- package/src/core/provider.ts +18 -1
- package/binding.gyp +0 -9
- package/dist/chunk-AL7ZD64M.js +0 -291
- package/dist/chunk-AL7ZD64M.js.map +0 -1
- package/dist/chunk-INQQQVXN.js +0 -310
- package/dist/chunk-INQQQVXN.js.map +0 -1
- package/dist/chunk-M7Z4P5MC.js +0 -64
- package/dist/chunk-M7Z4P5MC.js.map +0 -1
- package/dist/execution-logger-77TRRoWn.d.ts +0 -84
- package/dist/testing.js.map +0 -1
- package/dist/types-uulICZo7.d.ts +0 -216
- package/index.js +0 -1
package/dist/logger.js
CHANGED
|
@@ -1,821 +1,830 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
import { t as getExecutionLogger } from "./execution-logger-CoxSsBmU.js";
|
|
2
|
+
import { context, createContextKey, trace } from "@opentelemetry/api";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
//#region src/api/redact.ts
|
|
5
|
+
/**
|
|
6
|
+
* Redaction utility for structured log output
|
|
7
|
+
*
|
|
8
|
+
* Inspired by pino's redaction — zero-dependency, edge-compatible.
|
|
9
|
+
* Supports deep paths, wildcards, and custom censor values.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const redactor = createRedactor({
|
|
14
|
+
* paths: ['password', 'user.email', 'headers.authorization'],
|
|
15
|
+
* })
|
|
16
|
+
* const safe = redactor({ password: 'secret', user: { email: 'a@b.com' } })
|
|
17
|
+
* // { password: '[Redacted]', user: { email: '[Redacted]' } }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_CENSOR = "[Redacted]";
|
|
21
|
+
/**
|
|
22
|
+
* Validate and parse a dot/bracket path string into segments.
|
|
23
|
+
*
|
|
24
|
+
* Valid: "user.email", "users[*].name", "headers.*", "[0].secret"
|
|
25
|
+
* Invalid: "user..email", "user[", "foo.]bar", ""
|
|
26
|
+
*/
|
|
6
27
|
function parsePath(path) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
if (/\[\s*\]$/.test(path)) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`redact path has unclosed bracket: ${JSON.stringify(path)}`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
if (/\][^.[\]]/.test(path) && !/\]\[/.test(path)) {
|
|
35
|
-
const afterClose = path.match(/\]([^.[\]]+)/);
|
|
36
|
-
if (afterClose) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
`redact path has invalid characters after ']': ${JSON.stringify(path)}`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const segments = [];
|
|
43
|
-
const rx = /[^.[\]]+|\[(\d+|\*)\]/g;
|
|
44
|
-
let match;
|
|
45
|
-
let lastIndex = 0;
|
|
46
|
-
while ((match = rx.exec(path)) !== null) {
|
|
47
|
-
segments.push(match[1] ?? match[0]);
|
|
48
|
-
lastIndex = rx.lastIndex;
|
|
49
|
-
}
|
|
50
|
-
if (lastIndex < path.length) {
|
|
51
|
-
throw new Error(
|
|
52
|
-
`redact path has unexpected trailing content: ${JSON.stringify(path)}`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (segments.length === 0) {
|
|
56
|
-
throw new Error(
|
|
57
|
-
`redact path produced no segments: ${JSON.stringify(path)}`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
return segments;
|
|
28
|
+
if (typeof path !== "string" || path.length === 0) throw new Error(`redact path must be a non-empty string, got: ${JSON.stringify(path)}`);
|
|
29
|
+
if (path.includes("..")) throw new Error(`redact path contains empty segment: ${JSON.stringify(path)}`);
|
|
30
|
+
const openBracket = path.indexOf("[");
|
|
31
|
+
const closeBracket = path.indexOf("]");
|
|
32
|
+
if (openBracket !== -1 && closeBracket === -1 || closeBracket !== -1 && openBracket === -1) throw new Error(`redact path has unclosed bracket: ${JSON.stringify(path)}`);
|
|
33
|
+
if (openBracket !== -1 && closeBracket < openBracket) throw new Error(`redact path has bracket close before open: ${JSON.stringify(path)}`);
|
|
34
|
+
if (/\[\s*\]$/.test(path)) throw new Error(`redact path has unclosed bracket: ${JSON.stringify(path)}`);
|
|
35
|
+
if (/\][^.[\]]/.test(path) && !/\]\[/.test(path)) {
|
|
36
|
+
if (path.match(/\]([^.[\]]+)/)) throw new Error(`redact path has invalid characters after ']': ${JSON.stringify(path)}`);
|
|
37
|
+
}
|
|
38
|
+
const segments = [];
|
|
39
|
+
const rx = /[^.[\]]+|\[(\d+|\*)\]/g;
|
|
40
|
+
let match;
|
|
41
|
+
let lastIndex = 0;
|
|
42
|
+
while ((match = rx.exec(path)) !== null) {
|
|
43
|
+
segments.push(match[1] ?? match[0]);
|
|
44
|
+
lastIndex = rx.lastIndex;
|
|
45
|
+
}
|
|
46
|
+
if (lastIndex < path.length) throw new Error(`redact path has unexpected trailing content: ${JSON.stringify(path)}`);
|
|
47
|
+
if (segments.length === 0) throw new Error(`redact path produced no segments: ${JSON.stringify(path)}`);
|
|
48
|
+
return segments;
|
|
61
49
|
}
|
|
62
50
|
function isPlainObject(value) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
if (value === null || typeof value !== "object") return false;
|
|
52
|
+
const proto = Object.getPrototypeOf(value);
|
|
53
|
+
return proto === Object.prototype || proto === null;
|
|
66
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Deep clone plain objects and arrays only.
|
|
57
|
+
* Non-plain objects (Date, Map, Set, class instances) are returned by reference.
|
|
58
|
+
*/
|
|
67
59
|
function deepClone(value) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
return result;
|
|
60
|
+
if (value === null || typeof value !== "object") return value;
|
|
61
|
+
if (Array.isArray(value)) return value.map((v) => deepClone(v));
|
|
62
|
+
if (!isPlainObject(value)) return value;
|
|
63
|
+
const result = {};
|
|
64
|
+
for (const key of Object.keys(value)) result[key] = deepClone(value[key]);
|
|
65
|
+
return result;
|
|
76
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Insert a parsed path into the tree.
|
|
69
|
+
*
|
|
70
|
+
* buildPathTree([['user'], ['user', 'email']]) produces:
|
|
71
|
+
* { user: { redact: true, children: { email: { redact: true } } } }
|
|
72
|
+
*/
|
|
77
73
|
function buildPathTree(paths) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
return root;
|
|
74
|
+
const root = {};
|
|
75
|
+
for (const segments of paths) {
|
|
76
|
+
let node = root;
|
|
77
|
+
for (let i = 0; i < segments.length; i++) {
|
|
78
|
+
const seg = segments[i];
|
|
79
|
+
const isLast = i === segments.length - 1;
|
|
80
|
+
if (!node.children) node.children = {};
|
|
81
|
+
if (!node.children[seg]) node.children[seg] = {};
|
|
82
|
+
if (isLast) node.children[seg].redact = true;
|
|
83
|
+
node = node.children[seg];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return root;
|
|
93
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Walk a compiled path tree and redact matching values in `obj`.
|
|
90
|
+
* Mutates `obj` in place (caller should clone before calling).
|
|
91
|
+
*/
|
|
94
92
|
function redactWithTree(obj, node, censor, remove, path = []) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
126
|
-
const val = obj[key];
|
|
127
|
-
const currentPath = [...path, key];
|
|
128
|
-
if (childNode.redact && !childNode.children) {
|
|
129
|
-
applyCensor(obj, key, currentPath);
|
|
130
|
-
} else if (childNode.redact && childNode.children) {
|
|
131
|
-
if (val != null && typeof val === "object") {
|
|
132
|
-
redactWithTree(val, childNode, censor, remove, currentPath);
|
|
133
|
-
}
|
|
134
|
-
} else if (childNode.children && val != null && typeof val === "object") {
|
|
135
|
-
redactWithTree(val, childNode, censor, remove, currentPath);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
93
|
+
if (!node.children) return;
|
|
94
|
+
const applyCensor = (target, k, currentPath) => {
|
|
95
|
+
if (remove) delete target[k];
|
|
96
|
+
else target[k] = typeof censor === "function" ? censor(target[k], currentPath) : censor;
|
|
97
|
+
};
|
|
98
|
+
for (const [key, childNode] of Object.entries(node.children)) {
|
|
99
|
+
if (key === "*") {
|
|
100
|
+
for (const k of Object.keys(obj)) {
|
|
101
|
+
const wildcardPath = [...path, k];
|
|
102
|
+
if (childNode.redact && !childNode.children) applyCensor(obj, k, wildcardPath);
|
|
103
|
+
else if (childNode.redact && childNode.children) {
|
|
104
|
+
const child = obj[k];
|
|
105
|
+
if (child != null && typeof child === "object" && !Array.isArray(child)) redactWithTree(child, childNode, censor, remove, wildcardPath);
|
|
106
|
+
else applyCensor(obj, k, wildcardPath);
|
|
107
|
+
} else if (childNode.children) {
|
|
108
|
+
const child = obj[k];
|
|
109
|
+
if (child != null && typeof child === "object") redactWithTree(child, childNode, censor, remove, wildcardPath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
115
|
+
const val = obj[key];
|
|
116
|
+
const currentPath = [...path, key];
|
|
117
|
+
if (childNode.redact && !childNode.children) applyCensor(obj, key, currentPath);
|
|
118
|
+
else if (childNode.redact && childNode.children) {
|
|
119
|
+
if (val != null && typeof val === "object") redactWithTree(val, childNode, censor, remove, currentPath);
|
|
120
|
+
} else if (childNode.children && val != null && typeof val === "object") redactWithTree(val, childNode, censor, remove, currentPath);
|
|
121
|
+
}
|
|
138
122
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Paths that match the main autotel package's `sensitiveKey` pattern:
|
|
125
|
+
* `/^(password|passwd|pwd|secret|token|api[_-]?key|auth|credential|private[_-]?key|authorization)$/i`
|
|
126
|
+
*
|
|
127
|
+
* Copied here so the edge logger stays zero-dependency.
|
|
128
|
+
*/
|
|
129
|
+
const SENSITIVE_KEY_PATHS = [
|
|
130
|
+
"password",
|
|
131
|
+
"passwd",
|
|
132
|
+
"pwd",
|
|
133
|
+
"secret",
|
|
134
|
+
"token",
|
|
135
|
+
"apiKey",
|
|
136
|
+
"api_key",
|
|
137
|
+
"auth",
|
|
138
|
+
"credential",
|
|
139
|
+
"privateKey",
|
|
140
|
+
"private_key",
|
|
141
|
+
"authorization"
|
|
152
142
|
];
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Common request header paths that often carry secrets.
|
|
145
|
+
*/
|
|
146
|
+
const SENSITIVE_HEADER_PATHS = [
|
|
147
|
+
"req.headers.authorization",
|
|
148
|
+
"req.headers.cookie",
|
|
149
|
+
"headers.authorization",
|
|
150
|
+
"headers.cookie",
|
|
151
|
+
"request.headers.authorization",
|
|
152
|
+
"request.headers.cookie"
|
|
160
153
|
];
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Built-in redactor presets.
|
|
156
|
+
*
|
|
157
|
+
* - `default` — sensitive keys + request headers (passwords, tokens, secrets, auth headers)
|
|
158
|
+
* - `strict` — default + bearer/jwt/api-key patterns nested at any depth
|
|
159
|
+
* - `pci-dss` — credit card and payment-related fields
|
|
160
|
+
*/
|
|
161
|
+
const REDACT_PRESETS = {
|
|
162
|
+
/**
|
|
163
|
+
* Default: covers the same sensitive-key names as the main autotel package's
|
|
164
|
+
* `sensitiveKey` pattern, plus common request header paths.
|
|
165
|
+
*
|
|
166
|
+
* Redacted keys: password, passwd, pwd, secret, token, apiKey, auth,
|
|
167
|
+
* credential, privateKey, authorization
|
|
168
|
+
* Redacted headers: req.headers.authorization, req.headers.cookie, etc.
|
|
169
|
+
*/
|
|
170
|
+
default: { paths: [...SENSITIVE_KEY_PATHS, ...SENSITIVE_HEADER_PATHS] },
|
|
171
|
+
/**
|
|
172
|
+
* Strict: everything in default, plus nested paths for bearer tokens,
|
|
173
|
+
* JWT fields, and API key fields at common depths.
|
|
174
|
+
*/
|
|
175
|
+
strict: { paths: [
|
|
176
|
+
...SENSITIVE_KEY_PATHS,
|
|
177
|
+
...SENSITIVE_HEADER_PATHS,
|
|
178
|
+
"bearer",
|
|
179
|
+
"jwt",
|
|
180
|
+
"apiSecret",
|
|
181
|
+
"api_secret",
|
|
182
|
+
"accessToken",
|
|
183
|
+
"access_token",
|
|
184
|
+
"refreshToken",
|
|
185
|
+
"refresh_token",
|
|
186
|
+
"clientSecret",
|
|
187
|
+
"client_secret"
|
|
188
|
+
] },
|
|
189
|
+
/**
|
|
190
|
+
* PCI-DSS: credit card and payment-related fields.
|
|
191
|
+
*/
|
|
192
|
+
"pci-dss": { paths: [
|
|
193
|
+
"card",
|
|
194
|
+
"cardNumber",
|
|
195
|
+
"card_number",
|
|
196
|
+
"ccn",
|
|
197
|
+
"pan",
|
|
198
|
+
"cvv",
|
|
199
|
+
"cvc",
|
|
200
|
+
"expirationDate",
|
|
201
|
+
"expiration_date",
|
|
202
|
+
"exp",
|
|
203
|
+
"payment.card",
|
|
204
|
+
"payment.cardNumber",
|
|
205
|
+
"payment.cvv"
|
|
206
|
+
] }
|
|
214
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* Resolve a preset name or explicit options into RedactorOptions.
|
|
210
|
+
*/
|
|
215
211
|
function resolveConfig(config) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
return preset;
|
|
224
|
-
}
|
|
225
|
-
return config;
|
|
212
|
+
if (typeof config === "string") {
|
|
213
|
+
const preset = REDACT_PRESETS[config];
|
|
214
|
+
if (!preset) throw new Error(`Unknown redactor preset: "${config}". Available: ${Object.keys(REDACT_PRESETS).join(", ")}`);
|
|
215
|
+
return preset;
|
|
216
|
+
}
|
|
217
|
+
return config;
|
|
226
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Create a redactor function from a list of paths.
|
|
221
|
+
*
|
|
222
|
+
* Paths use dot notation for nested fields and `[*]` or `*` for wildcards:
|
|
223
|
+
* - `"password"` — top-level key
|
|
224
|
+
* - `"user.email"` — nested field
|
|
225
|
+
* - `"users[*].password"` or `"users.*.password"` — all items in array/object
|
|
226
|
+
* - `"headers.authorization"` — nested header
|
|
227
|
+
* - `"[*].secret"` — wildcard at root
|
|
228
|
+
*
|
|
229
|
+
* Overlapping paths are supported — e.g. `['user', 'user.email']` will
|
|
230
|
+
* redact `user` as a whole while still recursing into `user.email`.
|
|
231
|
+
*
|
|
232
|
+
* The returned function clones plain objects/arrays before mutating,
|
|
233
|
+
* so the original is never modified. Non-plain objects (Date, Map, etc.)
|
|
234
|
+
* are passed through by reference.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* // Using a preset
|
|
239
|
+
* const redactor = createRedactor('default')
|
|
240
|
+
*
|
|
241
|
+
* // Using explicit paths
|
|
242
|
+
* const redactor = createRedactor({
|
|
243
|
+
* paths: ['password', 'token', 'user.email', 'users[*].ssn'],
|
|
244
|
+
* censor: '[Filtered]', // optional, default '[Redacted]'
|
|
245
|
+
* })
|
|
246
|
+
*
|
|
247
|
+
* redactor({ password: 's3cret', user: { email: 'a@b.com' } })
|
|
248
|
+
* // → { password: '[Filtered]', user: { email: '[Filtered]' } }
|
|
249
|
+
*
|
|
250
|
+
* // Custom censor function:
|
|
251
|
+
* const mask = createRedactor({
|
|
252
|
+
* paths: ['ccn'],
|
|
253
|
+
* censor: (val) => '****' + String(val).slice(-4),
|
|
254
|
+
* })
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
227
257
|
function createRedactor(config) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
redactWithTree(clone, tree, censor, remove);
|
|
238
|
-
return clone;
|
|
239
|
-
};
|
|
258
|
+
const { paths, censor = DEFAULT_CENSOR, remove = false } = resolveConfig(config);
|
|
259
|
+
if (paths.length === 0) return (obj) => obj;
|
|
260
|
+
const tree = buildPathTree(paths.map((p) => parsePath(p)));
|
|
261
|
+
return function redactor(obj) {
|
|
262
|
+
if (obj == null || typeof obj !== "object") return obj;
|
|
263
|
+
const clone = deepClone(obj);
|
|
264
|
+
redactWithTree(clone, tree, censor, remove);
|
|
265
|
+
return clone;
|
|
266
|
+
};
|
|
240
267
|
}
|
|
241
268
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/api/logger.ts
|
|
271
|
+
/**
|
|
272
|
+
* Pino-compatible structured logger for edge environments.
|
|
273
|
+
*
|
|
274
|
+
* Supports the practical Pino surface used by app code:
|
|
275
|
+
* - Pino-style `LogFn` calls: string-first, object-first, and error-first
|
|
276
|
+
* - Child loggers, level control, custom levels, and `msgPrefix`
|
|
277
|
+
* - Serializers, formatters, mixins, redaction, and browser-style `transmit`
|
|
278
|
+
* - Trace context injection (`traceId`, `spanId`, `correlationId`)
|
|
279
|
+
* - Worker-safe EventEmitter-like methods for `level-change`
|
|
280
|
+
*
|
|
281
|
+
* The implementation stays dependency-light and console/write based so it
|
|
282
|
+
* works in Cloudflare Workers and similar edge runtimes.
|
|
283
|
+
*/
|
|
284
|
+
/**
|
|
285
|
+
* Context key for storing active log level (enables per-request log levels)
|
|
286
|
+
*/
|
|
287
|
+
const LOG_LEVEL_KEY = createContextKey("autotel-edge-log-level");
|
|
288
|
+
const PINO_LEVELS = {
|
|
289
|
+
values: {
|
|
290
|
+
trace: 10,
|
|
291
|
+
debug: 20,
|
|
292
|
+
info: 30,
|
|
293
|
+
warn: 40,
|
|
294
|
+
error: 50,
|
|
295
|
+
fatal: 60,
|
|
296
|
+
silent: Infinity
|
|
297
|
+
},
|
|
298
|
+
labels: {
|
|
299
|
+
10: "trace",
|
|
300
|
+
20: "debug",
|
|
301
|
+
30: "info",
|
|
302
|
+
40: "warn",
|
|
303
|
+
50: "error",
|
|
304
|
+
60: "fatal",
|
|
305
|
+
[Infinity]: "silent"
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const LOGGER_VERSION = "autotel-edge";
|
|
309
|
+
/**
|
|
310
|
+
* Get the active log level from context (if set)
|
|
311
|
+
* Falls back to undefined if no log level is set in context
|
|
312
|
+
*/
|
|
255
313
|
function getActiveLogLevel() {
|
|
256
|
-
|
|
314
|
+
return context.active().getValue(LOG_LEVEL_KEY);
|
|
257
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Run a function with a specific log level
|
|
318
|
+
* The log level is stored in OpenTelemetry context and applies to all logger calls within the callback
|
|
319
|
+
*
|
|
320
|
+
* This works in edge runtimes (uses OTel context, not Node.js AsyncLocalStorage)
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* // Enable debug logging for a specific request
|
|
325
|
+
* runWithLogLevel('debug', () => {
|
|
326
|
+
* log.debug('This will be logged')
|
|
327
|
+
* processRequest()
|
|
328
|
+
* })
|
|
329
|
+
*
|
|
330
|
+
* // Disable logging temporarily
|
|
331
|
+
* runWithLogLevel('silent', () => {
|
|
332
|
+
* log.info('This will NOT be logged')
|
|
333
|
+
* })
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
258
336
|
function runWithLogLevel(level, callback) {
|
|
259
|
-
|
|
260
|
-
|
|
337
|
+
const ctx = context.active().setValue(LOG_LEVEL_KEY, level);
|
|
338
|
+
return context.with(ctx, callback);
|
|
261
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Get current trace context from active span
|
|
342
|
+
*/
|
|
262
343
|
function getTraceContext() {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
};
|
|
344
|
+
const span = trace.getActiveSpan();
|
|
345
|
+
if (!span) return null;
|
|
346
|
+
const ctx = span.spanContext();
|
|
347
|
+
return {
|
|
348
|
+
traceId: ctx.traceId,
|
|
349
|
+
spanId: ctx.spanId,
|
|
350
|
+
correlationId: ctx.traceId.slice(0, 16)
|
|
351
|
+
};
|
|
272
352
|
}
|
|
273
353
|
function isRecord(value) {
|
|
274
|
-
|
|
354
|
+
return typeof value === "object" && value !== null;
|
|
275
355
|
}
|
|
276
356
|
function toErrorAttrs(error) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return { error: { message: String(error), type: "Error" } };
|
|
357
|
+
if (error instanceof Error) return { error: {
|
|
358
|
+
message: error.message,
|
|
359
|
+
type: error.name,
|
|
360
|
+
stack: error.stack
|
|
361
|
+
} };
|
|
362
|
+
return { error: {
|
|
363
|
+
message: String(error),
|
|
364
|
+
type: "Error"
|
|
365
|
+
} };
|
|
287
366
|
}
|
|
288
367
|
function formatMessage(template, args) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return JSON.stringify(value);
|
|
315
|
-
} catch {
|
|
316
|
-
return String(value);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
default: {
|
|
320
|
-
return token;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
return formatted;
|
|
368
|
+
if (args.length === 0) return template;
|
|
369
|
+
let argIndex = 0;
|
|
370
|
+
return template.replaceAll(/%[sdjifoO%]/g, (token) => {
|
|
371
|
+
if (token === "%%") return "%";
|
|
372
|
+
if (argIndex >= args.length) return token;
|
|
373
|
+
const value = args[argIndex++];
|
|
374
|
+
switch (token) {
|
|
375
|
+
case "%d":
|
|
376
|
+
case "%i":
|
|
377
|
+
case "%f": return String(Number(value));
|
|
378
|
+
case "%j": try {
|
|
379
|
+
return JSON.stringify(value);
|
|
380
|
+
} catch {
|
|
381
|
+
return "[Circular]";
|
|
382
|
+
}
|
|
383
|
+
case "%s": return String(value);
|
|
384
|
+
case "%o":
|
|
385
|
+
case "%O": try {
|
|
386
|
+
return JSON.stringify(value);
|
|
387
|
+
} catch {
|
|
388
|
+
return String(value);
|
|
389
|
+
}
|
|
390
|
+
default: return token;
|
|
391
|
+
}
|
|
392
|
+
});
|
|
325
393
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
info: "\x1B[36m",
|
|
337
|
-
// cyan
|
|
338
|
-
debug: "\x1B[34m",
|
|
339
|
-
// blue
|
|
340
|
-
trace: "\x1B[90m"
|
|
341
|
-
// gray
|
|
394
|
+
const ANSI_RESET = "\x1B[0m";
|
|
395
|
+
const ANSI_DIM = "\x1B[2m";
|
|
396
|
+
const ANSI_BOLD = "\x1B[1m";
|
|
397
|
+
const ANSI_COLORS = {
|
|
398
|
+
fatal: "\x1B[41m\x1B[37m",
|
|
399
|
+
error: "\x1B[31m",
|
|
400
|
+
warn: "\x1B[33m",
|
|
401
|
+
info: "\x1B[36m",
|
|
402
|
+
debug: "\x1B[34m",
|
|
403
|
+
trace: "\x1B[90m"
|
|
342
404
|
};
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
405
|
+
const LEVEL_SYMBOLS = {
|
|
406
|
+
fatal: "✗",
|
|
407
|
+
error: "✗",
|
|
408
|
+
warn: "⚠",
|
|
409
|
+
info: "●",
|
|
410
|
+
debug: "◦",
|
|
411
|
+
trace: "…"
|
|
350
412
|
};
|
|
351
413
|
function formatPrettyTimestamp() {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const m = String(d.getMinutes()).padStart(2, "0");
|
|
355
|
-
const s = String(d.getSeconds()).padStart(2, "0");
|
|
356
|
-
const ms = String(d.getMilliseconds()).padStart(3, "0");
|
|
357
|
-
return `${h}:${m}:${s}.${ms}`;
|
|
414
|
+
const d = /* @__PURE__ */ new Date();
|
|
415
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(3, "0")}`;
|
|
358
416
|
}
|
|
359
417
|
function formatPrettyAttrs(attrs) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return "\n" + JSON.stringify(attrs, null, 2).split("\n").map((line) => ` ${line}`).join("\n");
|
|
418
|
+
if (Object.keys(attrs).length === 0) return "";
|
|
419
|
+
return "\n" + JSON.stringify(attrs, null, 2).split("\n").map((line) => ` ${line}`).join("\n");
|
|
363
420
|
}
|
|
364
421
|
function safeStringify(obj, depthLimit, edgeLimit) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return value;
|
|
387
|
-
});
|
|
422
|
+
const ancestors = [];
|
|
423
|
+
return JSON.stringify(obj, function replacer(_key, value) {
|
|
424
|
+
if (typeof value === "object" && value !== null) {
|
|
425
|
+
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) ancestors.pop();
|
|
426
|
+
if (ancestors.includes(value)) return "[Circular]";
|
|
427
|
+
if (ancestors.length >= depthLimit) return "[Object]";
|
|
428
|
+
ancestors.push(value);
|
|
429
|
+
if (!Array.isArray(value)) {
|
|
430
|
+
const keys = Object.keys(value);
|
|
431
|
+
if (keys.length > edgeLimit) {
|
|
432
|
+
const truncated = {};
|
|
433
|
+
for (let i = 0; i < edgeLimit; i++) truncated[keys[i]] = value[keys[i]];
|
|
434
|
+
truncated["..."] = `[${keys.length - edgeLimit} more properties]`;
|
|
435
|
+
return truncated;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return value;
|
|
440
|
+
});
|
|
388
441
|
}
|
|
389
442
|
function parseLogArgs(args) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
return {
|
|
410
|
-
msg: String(first ?? ""),
|
|
411
|
-
attrs: void 0
|
|
412
|
-
};
|
|
443
|
+
const [first, ...rest] = args;
|
|
444
|
+
if (typeof first === "string") return {
|
|
445
|
+
msg: formatMessage(first, rest),
|
|
446
|
+
attrs: void 0
|
|
447
|
+
};
|
|
448
|
+
if (first instanceof Error) return {
|
|
449
|
+
msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : first.message,
|
|
450
|
+
attrs: toErrorAttrs(first)
|
|
451
|
+
};
|
|
452
|
+
if (isRecord(first)) return {
|
|
453
|
+
msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : "",
|
|
454
|
+
attrs: first
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
msg: String(first ?? ""),
|
|
458
|
+
attrs: void 0
|
|
459
|
+
};
|
|
413
460
|
}
|
|
414
461
|
function createEdgeLogger(service, options) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
},
|
|
764
|
-
once: (event, listener) => {
|
|
765
|
-
const wrapped = (...args) => {
|
|
766
|
-
removeEventListener(event, wrapped);
|
|
767
|
-
listener(...args);
|
|
768
|
-
};
|
|
769
|
-
addListener(event, wrapped);
|
|
770
|
-
return logger;
|
|
771
|
-
},
|
|
772
|
-
prependListener: (event, listener) => {
|
|
773
|
-
addListener(event, listener, true);
|
|
774
|
-
return logger;
|
|
775
|
-
},
|
|
776
|
-
prependOnceListener: (event, listener) => {
|
|
777
|
-
const wrapped = (...args) => {
|
|
778
|
-
removeEventListener(event, wrapped);
|
|
779
|
-
listener(...args);
|
|
780
|
-
};
|
|
781
|
-
addListener(event, wrapped, true);
|
|
782
|
-
return logger;
|
|
783
|
-
},
|
|
784
|
-
rawListeners: (event) => [...listeners.get(event) ?? []],
|
|
785
|
-
off: (event, listener) => {
|
|
786
|
-
removeEventListener(event, listener);
|
|
787
|
-
return logger;
|
|
788
|
-
},
|
|
789
|
-
removeAllListeners: (event) => {
|
|
790
|
-
if (event === void 0) {
|
|
791
|
-
listeners.clear();
|
|
792
|
-
levelListeners.length = 0;
|
|
793
|
-
return logger;
|
|
794
|
-
}
|
|
795
|
-
listeners.delete(event);
|
|
796
|
-
if (event === "level-change") {
|
|
797
|
-
levelListeners.length = 0;
|
|
798
|
-
}
|
|
799
|
-
return logger;
|
|
800
|
-
},
|
|
801
|
-
removeListener: (event, listener) => {
|
|
802
|
-
removeEventListener(event, listener);
|
|
803
|
-
return logger;
|
|
804
|
-
},
|
|
805
|
-
setMaxListeners: (n) => {
|
|
806
|
-
maxListeners = n;
|
|
807
|
-
return logger;
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
for (const levelName of Object.keys(customLevels)) {
|
|
811
|
-
logger[levelName] = makeLogMethod(levelName);
|
|
812
|
-
}
|
|
813
|
-
return logger;
|
|
462
|
+
const customLevels = { ...options?.customLevels };
|
|
463
|
+
const availableLevels = options?.useOnlyCustomLevels ? {
|
|
464
|
+
...customLevels,
|
|
465
|
+
silent: Infinity
|
|
466
|
+
} : {
|
|
467
|
+
...PINO_LEVELS.values,
|
|
468
|
+
...customLevels
|
|
469
|
+
};
|
|
470
|
+
let currentLevel = options?.level ?? (options?.useOnlyCustomLevels ? Object.keys(customLevels)[0] : "info") ?? "info";
|
|
471
|
+
const pretty = options?.pretty || false;
|
|
472
|
+
const currentBindings = { ...options?.bindings };
|
|
473
|
+
const resolvedRedact = Array.isArray(options?.redact) ? { paths: options.redact } : options?.redact;
|
|
474
|
+
const redactor = resolvedRedact ? createRedactor(resolvedRedact) : null;
|
|
475
|
+
const msgPrefix = options?.msgPrefix;
|
|
476
|
+
const levelValues = availableLevels;
|
|
477
|
+
const levelListeners = [];
|
|
478
|
+
const onChildCallback = options?.onChild ?? (() => {});
|
|
479
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
480
|
+
let maxListeners = 10;
|
|
481
|
+
const messageKey = options?.messageKey ?? "msg";
|
|
482
|
+
const errorKey = options?.errorKey ?? "err";
|
|
483
|
+
const nestedKey = options?.nestedKey;
|
|
484
|
+
const serializers = { ...options?.serializers };
|
|
485
|
+
const bindingsFormatter = options?.formatters?.bindings;
|
|
486
|
+
const levelFormatter = options?.formatters?.level;
|
|
487
|
+
const logFormatter = options?.formatters?.log;
|
|
488
|
+
const mixin = options?.mixin;
|
|
489
|
+
const levelComparison = options?.levelComparison;
|
|
490
|
+
const mixinMergeStrategy = options?.mixinMergeStrategy ?? ((mergeObject, mixinObject) => ({
|
|
491
|
+
...mergeObject,
|
|
492
|
+
...mixinObject
|
|
493
|
+
}));
|
|
494
|
+
let enabled = options?.enabled ?? true;
|
|
495
|
+
const loggerName = options?.name;
|
|
496
|
+
const base = options?.base;
|
|
497
|
+
const timestamp = options?.timestamp ?? true;
|
|
498
|
+
const safe = options?.safe ?? true;
|
|
499
|
+
const crlf = options?.crlf ?? false;
|
|
500
|
+
const depthLimit = options?.depthLimit ?? 5;
|
|
501
|
+
const edgeLimitOpt = options?.edgeLimit ?? 100;
|
|
502
|
+
const hooks = options?.hooks;
|
|
503
|
+
const writeFn = options?.write;
|
|
504
|
+
const transmit = options?.transmit;
|
|
505
|
+
const bindingsChain = options?._bindingsChain ?? [];
|
|
506
|
+
const compareLevels = (current, expected, comparison) => {
|
|
507
|
+
if (typeof comparison === "function") return comparison(current, expected);
|
|
508
|
+
if (comparison === "DESC") return current <= expected;
|
|
509
|
+
return current >= expected;
|
|
510
|
+
};
|
|
511
|
+
const addListener = (event, listener, prepend = false) => {
|
|
512
|
+
const activeListeners = listeners.get(event) ?? [];
|
|
513
|
+
if (prepend) activeListeners.unshift(listener);
|
|
514
|
+
else activeListeners.push(listener);
|
|
515
|
+
listeners.set(event, activeListeners);
|
|
516
|
+
if (event === "level-change") {
|
|
517
|
+
levelListeners.length = 0;
|
|
518
|
+
levelListeners.push(...listeners.get(event) ?? []);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
const notifyLevelChange = (nextLevel, previousLevel, instance) => {
|
|
522
|
+
const nextValue = levelValues[nextLevel] ?? Infinity;
|
|
523
|
+
const previousValue = levelValues[previousLevel] ?? Infinity;
|
|
524
|
+
const snapshot = [...levelListeners];
|
|
525
|
+
for (const listener of snapshot) listener(nextLevel, nextValue, previousLevel, previousValue, instance);
|
|
526
|
+
};
|
|
527
|
+
const removeEventListener = (event, listener) => {
|
|
528
|
+
const activeListeners = listeners.get(event);
|
|
529
|
+
if (!activeListeners) return;
|
|
530
|
+
const index = activeListeners.indexOf(listener);
|
|
531
|
+
if (index !== -1) activeListeners.splice(index, 1);
|
|
532
|
+
if (activeListeners.length === 0) listeners.delete(event);
|
|
533
|
+
else listeners.set(event, activeListeners);
|
|
534
|
+
if (event === "level-change") {
|
|
535
|
+
levelListeners.length = 0;
|
|
536
|
+
levelListeners.push(...listeners.get(event) ?? []);
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const shouldLog = (level) => {
|
|
540
|
+
if (!enabled) return false;
|
|
541
|
+
const activeLevel = getActiveLogLevel() ?? currentLevel;
|
|
542
|
+
if (activeLevel === "silent") return false;
|
|
543
|
+
const currentValue = levelValues[activeLevel];
|
|
544
|
+
const expectedValue = levelValues[level];
|
|
545
|
+
if (currentValue === void 0 || expectedValue === void 0) return false;
|
|
546
|
+
return compareLevels(expectedValue, currentValue, levelComparison);
|
|
547
|
+
};
|
|
548
|
+
const stringify = (obj) => {
|
|
549
|
+
if (safe) return safeStringify(obj, depthLimit, edgeLimitOpt);
|
|
550
|
+
return JSON.stringify(obj);
|
|
551
|
+
};
|
|
552
|
+
const getTimestamp = () => {
|
|
553
|
+
if (timestamp === false) return void 0;
|
|
554
|
+
if (typeof timestamp === "function") return timestamp();
|
|
555
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
556
|
+
};
|
|
557
|
+
const writeOutput = (level, logObject) => {
|
|
558
|
+
if (writeFn) if (typeof writeFn === "function") writeFn(logObject);
|
|
559
|
+
else if (typeof writeFn === "object" && writeFn[level]) writeFn[level](logObject);
|
|
560
|
+
else console.log(stringify(logObject));
|
|
561
|
+
else if (pretty) return;
|
|
562
|
+
else {
|
|
563
|
+
const json = stringify(logObject);
|
|
564
|
+
console.log(crlf ? json + "\r\n" : json);
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
const applySerializers = (obj) => {
|
|
568
|
+
if (Object.keys(serializers).length === 0) return obj;
|
|
569
|
+
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, key in serializers ? serializers[key](value) : value]));
|
|
570
|
+
};
|
|
571
|
+
const emitTransmit = (level, rawArgs) => {
|
|
572
|
+
if (!transmit) return;
|
|
573
|
+
if (transmit.level) {
|
|
574
|
+
const transmitValue = levelValues[transmit.level];
|
|
575
|
+
const logValue = levelValues[level];
|
|
576
|
+
if (transmitValue !== void 0 && logValue !== void 0 && logValue < transmitValue) return;
|
|
577
|
+
}
|
|
578
|
+
const processedMessages = rawArgs.map((m) => {
|
|
579
|
+
if (typeof m !== "object" || m === null) return m;
|
|
580
|
+
const serialized = applySerializers(m);
|
|
581
|
+
return redactor ? redactor(serialized) : serialized;
|
|
582
|
+
});
|
|
583
|
+
const processedBindings = bindingsChain.map((b) => {
|
|
584
|
+
const serialized = applySerializers({ ...b });
|
|
585
|
+
return redactor ? redactor(serialized) : serialized;
|
|
586
|
+
});
|
|
587
|
+
const logEvent = {
|
|
588
|
+
ts: Date.now(),
|
|
589
|
+
messages: processedMessages,
|
|
590
|
+
bindings: processedBindings,
|
|
591
|
+
level: {
|
|
592
|
+
label: level,
|
|
593
|
+
value: levelValues[level]
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
transmit.send(level, logEvent);
|
|
597
|
+
};
|
|
598
|
+
const log = (level, msg, attrs, rawArgs) => {
|
|
599
|
+
if (!shouldLog(level)) return;
|
|
600
|
+
const ctx = getTraceContext();
|
|
601
|
+
const message = msgPrefix ? `${msgPrefix}${msg}` : msg;
|
|
602
|
+
const baseBindings = bindingsFormatter ? bindingsFormatter({ ...currentBindings }) : { ...currentBindings };
|
|
603
|
+
const serializedAttrs = attrs ? Object.fromEntries(Object.entries(attrs).map(([key, value]) => [key, key in serializers ? serializers[key](value) : value])) : void 0;
|
|
604
|
+
const mergeObject = nestedKey ? { [nestedKey]: serializedAttrs ?? {} } : serializedAttrs ?? {};
|
|
605
|
+
const mergedLogObject = mixinMergeStrategy(mergeObject, mixin?.(mergeObject, levelValues[level], logger) ?? {});
|
|
606
|
+
const formattedLogObject = logFormatter ? logFormatter(mergedLogObject) : mergedLogObject;
|
|
607
|
+
const formattedLevel = levelFormatter ? levelFormatter(level, levelValues[level]) : { level: logger.useLevelLabels ? level : levelValues[level] };
|
|
608
|
+
const ts = getTimestamp();
|
|
609
|
+
const logEntry = {
|
|
610
|
+
...formattedLevel,
|
|
611
|
+
...loggerName === void 0 ? {} : { name: loggerName },
|
|
612
|
+
service,
|
|
613
|
+
...message === "" ? {} : { [messageKey]: message },
|
|
614
|
+
...base === null || base === void 0 ? {} : base,
|
|
615
|
+
...baseBindings,
|
|
616
|
+
...formattedLogObject,
|
|
617
|
+
...ctx,
|
|
618
|
+
...ts === void 0 ? {} : { timestamp: ts }
|
|
619
|
+
};
|
|
620
|
+
if (serializedAttrs && !nestedKey && errorKey !== "error" && "error" in logEntry) {
|
|
621
|
+
logEntry[errorKey] = logEntry.error;
|
|
622
|
+
delete logEntry.error;
|
|
623
|
+
}
|
|
624
|
+
if (pretty && !writeFn) {
|
|
625
|
+
const color = ANSI_COLORS[level] ?? ANSI_COLORS.info;
|
|
626
|
+
const symbol = LEVEL_SYMBOLS[level] ?? "●";
|
|
627
|
+
const time = formatPrettyTimestamp();
|
|
628
|
+
const traceInfo = ctx ? ` ${ANSI_DIM}[${ctx.traceId.slice(0, 8)}/${ctx.spanId.slice(0, 8)}]${ANSI_RESET}` : "";
|
|
629
|
+
const prettyAttrs = {
|
|
630
|
+
...baseBindings,
|
|
631
|
+
...formattedLogObject
|
|
632
|
+
};
|
|
633
|
+
const attrsStr = formatPrettyAttrs(redactor ? redactor(prettyAttrs) : prettyAttrs);
|
|
634
|
+
console.log(`${ANSI_DIM}${time}${ANSI_RESET} ${color}${ANSI_BOLD}${symbol} ${level.toUpperCase()}${ANSI_RESET}${traceInfo} ${ANSI_DIM}(${service})${ANSI_RESET} ${message}${attrsStr}`);
|
|
635
|
+
} else writeOutput(level, redactor ? redactor(logEntry) : logEntry);
|
|
636
|
+
emitTransmit(level, rawArgs ?? []);
|
|
637
|
+
};
|
|
638
|
+
const makeLogMethod = (levelName) => {
|
|
639
|
+
return ((...args) => {
|
|
640
|
+
if (hooks?.logMethod) {
|
|
641
|
+
const method = ((...methodArgs) => {
|
|
642
|
+
const { msg, attrs } = parseLogArgs(methodArgs);
|
|
643
|
+
log(levelName, msg, attrs, methodArgs);
|
|
644
|
+
});
|
|
645
|
+
hooks.logMethod.call(logger, args, method, levelValues[levelName]);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const { msg, attrs } = parseLogArgs(args);
|
|
649
|
+
log(levelName, msg, attrs, args);
|
|
650
|
+
});
|
|
651
|
+
};
|
|
652
|
+
const logger = {
|
|
653
|
+
version: LOGGER_VERSION,
|
|
654
|
+
get name() {
|
|
655
|
+
return loggerName;
|
|
656
|
+
},
|
|
657
|
+
levels: {
|
|
658
|
+
values: levelValues,
|
|
659
|
+
labels: Object.fromEntries(Object.entries(levelValues).map(([label, value]) => [value, label]))
|
|
660
|
+
},
|
|
661
|
+
useLevelLabels: options?.useLevelLabels ?? false,
|
|
662
|
+
customLevels: { ...customLevels },
|
|
663
|
+
useOnlyCustomLevels: options?.useOnlyCustomLevels ?? false,
|
|
664
|
+
get level() {
|
|
665
|
+
return currentLevel;
|
|
666
|
+
},
|
|
667
|
+
set level(level) {
|
|
668
|
+
const previousLevel = currentLevel;
|
|
669
|
+
currentLevel = level;
|
|
670
|
+
notifyLevelChange(level, previousLevel, logger);
|
|
671
|
+
},
|
|
672
|
+
get levelVal() {
|
|
673
|
+
return levelValues[currentLevel] ?? Infinity;
|
|
674
|
+
},
|
|
675
|
+
get msgPrefix() {
|
|
676
|
+
return msgPrefix;
|
|
677
|
+
},
|
|
678
|
+
onChild: onChildCallback,
|
|
679
|
+
get enabled() {
|
|
680
|
+
return enabled;
|
|
681
|
+
},
|
|
682
|
+
set enabled(value) {
|
|
683
|
+
enabled = value;
|
|
684
|
+
},
|
|
685
|
+
info: makeLogMethod("info"),
|
|
686
|
+
error: makeLogMethod("error"),
|
|
687
|
+
warn: makeLogMethod("warn"),
|
|
688
|
+
debug: makeLogMethod("debug"),
|
|
689
|
+
trace: makeLogMethod("trace"),
|
|
690
|
+
fatal: makeLogMethod("fatal"),
|
|
691
|
+
silent: (() => {}),
|
|
692
|
+
child: (bindings, childOptions) => {
|
|
693
|
+
const childLogger = createEdgeLogger(service, {
|
|
694
|
+
level: childOptions?.level ?? currentLevel,
|
|
695
|
+
pretty,
|
|
696
|
+
bindings: {
|
|
697
|
+
...currentBindings,
|
|
698
|
+
...bindings
|
|
699
|
+
},
|
|
700
|
+
redact: childOptions?.redact ?? options?.redact,
|
|
701
|
+
msgPrefix: childOptions?.msgPrefix === void 0 ? msgPrefix : `${msgPrefix ?? ""}${childOptions.msgPrefix}`,
|
|
702
|
+
useLevelLabels: logger.useLevelLabels,
|
|
703
|
+
onChild: onChildCallback,
|
|
704
|
+
enabled,
|
|
705
|
+
messageKey,
|
|
706
|
+
errorKey,
|
|
707
|
+
nestedKey,
|
|
708
|
+
serializers: {
|
|
709
|
+
...serializers,
|
|
710
|
+
...childOptions?.serializers
|
|
711
|
+
},
|
|
712
|
+
formatters: {
|
|
713
|
+
level: childOptions?.formatters?.level ?? levelFormatter,
|
|
714
|
+
bindings: childOptions?.formatters?.bindings ?? bindingsFormatter,
|
|
715
|
+
log: childOptions?.formatters?.log ?? logFormatter
|
|
716
|
+
},
|
|
717
|
+
mixin,
|
|
718
|
+
mixinMergeStrategy,
|
|
719
|
+
customLevels,
|
|
720
|
+
useOnlyCustomLevels: options?.useOnlyCustomLevels,
|
|
721
|
+
levelComparison: options?.levelComparison,
|
|
722
|
+
name: loggerName,
|
|
723
|
+
base,
|
|
724
|
+
timestamp,
|
|
725
|
+
safe,
|
|
726
|
+
crlf,
|
|
727
|
+
depthLimit,
|
|
728
|
+
edgeLimit: edgeLimitOpt,
|
|
729
|
+
hooks,
|
|
730
|
+
write: writeFn,
|
|
731
|
+
transmit,
|
|
732
|
+
_bindingsChain: [...bindingsChain, bindings]
|
|
733
|
+
});
|
|
734
|
+
onChildCallback(childLogger);
|
|
735
|
+
return childLogger;
|
|
736
|
+
},
|
|
737
|
+
isLevelEnabled: (level) => shouldLog(level),
|
|
738
|
+
bindings: () => ({ ...currentBindings }),
|
|
739
|
+
setBindings: (bindings) => {
|
|
740
|
+
for (const [key, value] of Object.entries(bindings)) if (!(key in currentBindings)) currentBindings[key] = value;
|
|
741
|
+
},
|
|
742
|
+
flush: (cb) => {
|
|
743
|
+
cb?.();
|
|
744
|
+
},
|
|
745
|
+
emit: (event, ...args) => {
|
|
746
|
+
const activeListeners = listeners.get(event);
|
|
747
|
+
if (!activeListeners || activeListeners.length === 0) return false;
|
|
748
|
+
const snapshot = [...activeListeners];
|
|
749
|
+
for (const listener of snapshot) listener(...args);
|
|
750
|
+
return true;
|
|
751
|
+
},
|
|
752
|
+
eventNames: () => [...listeners.keys()],
|
|
753
|
+
getMaxListeners: () => maxListeners,
|
|
754
|
+
listenerCount: (event) => listeners.get(event)?.length ?? 0,
|
|
755
|
+
listeners: (event) => [...listeners.get(event) ?? []],
|
|
756
|
+
on: (event, listener) => {
|
|
757
|
+
addListener(event, listener);
|
|
758
|
+
return logger;
|
|
759
|
+
},
|
|
760
|
+
addListener: (event, listener) => {
|
|
761
|
+
addListener(event, listener);
|
|
762
|
+
return logger;
|
|
763
|
+
},
|
|
764
|
+
once: (event, listener) => {
|
|
765
|
+
const wrapped = (...args) => {
|
|
766
|
+
removeEventListener(event, wrapped);
|
|
767
|
+
listener(...args);
|
|
768
|
+
};
|
|
769
|
+
addListener(event, wrapped);
|
|
770
|
+
return logger;
|
|
771
|
+
},
|
|
772
|
+
prependListener: (event, listener) => {
|
|
773
|
+
addListener(event, listener, true);
|
|
774
|
+
return logger;
|
|
775
|
+
},
|
|
776
|
+
prependOnceListener: (event, listener) => {
|
|
777
|
+
const wrapped = (...args) => {
|
|
778
|
+
removeEventListener(event, wrapped);
|
|
779
|
+
listener(...args);
|
|
780
|
+
};
|
|
781
|
+
addListener(event, wrapped, true);
|
|
782
|
+
return logger;
|
|
783
|
+
},
|
|
784
|
+
rawListeners: (event) => [...listeners.get(event) ?? []],
|
|
785
|
+
off: (event, listener) => {
|
|
786
|
+
removeEventListener(event, listener);
|
|
787
|
+
return logger;
|
|
788
|
+
},
|
|
789
|
+
removeAllListeners: (event) => {
|
|
790
|
+
if (event === void 0) {
|
|
791
|
+
listeners.clear();
|
|
792
|
+
levelListeners.length = 0;
|
|
793
|
+
return logger;
|
|
794
|
+
}
|
|
795
|
+
listeners.delete(event);
|
|
796
|
+
if (event === "level-change") levelListeners.length = 0;
|
|
797
|
+
return logger;
|
|
798
|
+
},
|
|
799
|
+
removeListener: (event, listener) => {
|
|
800
|
+
removeEventListener(event, listener);
|
|
801
|
+
return logger;
|
|
802
|
+
},
|
|
803
|
+
setMaxListeners: (n) => {
|
|
804
|
+
maxListeners = n;
|
|
805
|
+
return logger;
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
for (const levelName of Object.keys(customLevels)) logger[levelName] = makeLogMethod(levelName);
|
|
809
|
+
return logger;
|
|
814
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* Helper to get trace context (useful for BYOL - Bring Your Own Logger)
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* import bunyan from 'bunyan'
|
|
817
|
+
* import { getEdgeTraceContext } from 'autotel-edge/api/logger'
|
|
818
|
+
*
|
|
819
|
+
* const bunyanLogger = bunyan.createLogger({ name: 'myapp' })
|
|
820
|
+
* const ctx = getEdgeTraceContext()
|
|
821
|
+
* bunyanLogger.info({ ...ctx, email: 'test@example.com' }, 'Creating user')
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
815
824
|
function getEdgeTraceContext() {
|
|
816
|
-
|
|
825
|
+
return getTraceContext();
|
|
817
826
|
}
|
|
818
827
|
|
|
819
|
-
|
|
820
|
-
|
|
828
|
+
//#endregion
|
|
829
|
+
export { REDACT_PRESETS, createEdgeLogger, createRedactor, getActiveLogLevel, getEdgeTraceContext, getExecutionLogger, runWithLogLevel };
|
|
821
830
|
//# sourceMappingURL=logger.js.map
|