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.
Files changed (45) hide show
  1. package/dist/config-0FktBGyj.js +290 -0
  2. package/dist/config-0FktBGyj.js.map +1 -0
  3. package/dist/events.d.ts +42 -43
  4. package/dist/events.d.ts.map +1 -0
  5. package/dist/events.js +144 -137
  6. package/dist/events.js.map +1 -1
  7. package/dist/execution-logger-BWlya70r.d.ts +137 -0
  8. package/dist/execution-logger-BWlya70r.d.ts.map +1 -0
  9. package/dist/execution-logger-CoxSsBmU.js +247 -0
  10. package/dist/execution-logger-CoxSsBmU.js.map +1 -0
  11. package/dist/index.d.ts +192 -276
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +930 -1060
  14. package/dist/index.js.map +1 -1
  15. package/dist/logger.d.ts +122 -136
  16. package/dist/logger.d.ts.map +1 -0
  17. package/dist/logger.js +783 -774
  18. package/dist/logger.js.map +1 -1
  19. package/dist/parse-error.d.ts +12 -10
  20. package/dist/parse-error.d.ts.map +1 -0
  21. package/dist/parse-error.js +55 -2
  22. package/dist/parse-error.js.map +1 -1
  23. package/dist/sampling.d.ts +51 -76
  24. package/dist/sampling.d.ts.map +1 -0
  25. package/dist/sampling.js +155 -90
  26. package/dist/sampling.js.map +1 -1
  27. package/dist/testing.d.ts +1 -2
  28. package/dist/testing.js +1 -3
  29. package/dist/types-HCbsjZzp.d.ts +214 -0
  30. package/dist/types-HCbsjZzp.d.ts.map +1 -0
  31. package/package.json +6 -6
  32. package/src/core/buffer.ts +4 -3
  33. package/src/core/provider.context.test.ts +55 -0
  34. package/src/core/provider.ts +18 -1
  35. package/binding.gyp +0 -9
  36. package/dist/chunk-AL7ZD64M.js +0 -291
  37. package/dist/chunk-AL7ZD64M.js.map +0 -1
  38. package/dist/chunk-INQQQVXN.js +0 -310
  39. package/dist/chunk-INQQQVXN.js.map +0 -1
  40. package/dist/chunk-M7Z4P5MC.js +0 -64
  41. package/dist/chunk-M7Z4P5MC.js.map +0 -1
  42. package/dist/execution-logger-77TRRoWn.d.ts +0 -84
  43. package/dist/testing.js.map +0 -1
  44. package/dist/types-uulICZo7.d.ts +0 -216
  45. package/index.js +0 -1
package/dist/logger.js CHANGED
@@ -1,821 +1,830 @@
1
- export { getExecutionLogger } from './chunk-AL7ZD64M.js';
2
- import { createContextKey, context, trace } from '@opentelemetry/api';
1
+ import { t as getExecutionLogger } from "./execution-logger-CoxSsBmU.js";
2
+ import { context, createContextKey, trace } from "@opentelemetry/api";
3
3
 
4
- // src/api/redact.ts
5
- var DEFAULT_CENSOR = "[Redacted]";
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
- if (typeof path !== "string" || path.length === 0) {
8
- throw new Error(
9
- `redact path must be a non-empty string, got: ${JSON.stringify(path)}`
10
- );
11
- }
12
- if (path.includes("..")) {
13
- throw new Error(
14
- `redact path contains empty segment: ${JSON.stringify(path)}`
15
- );
16
- }
17
- const openBracket = path.indexOf("[");
18
- const closeBracket = path.indexOf("]");
19
- if (openBracket !== -1 && closeBracket === -1 || closeBracket !== -1 && openBracket === -1) {
20
- throw new Error(
21
- `redact path has unclosed bracket: ${JSON.stringify(path)}`
22
- );
23
- }
24
- if (openBracket !== -1 && closeBracket < openBracket) {
25
- throw new Error(
26
- `redact path has bracket close before open: ${JSON.stringify(path)}`
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
- if (value === null || typeof value !== "object") return false;
64
- const proto = Object.getPrototypeOf(value);
65
- return proto === Object.prototype || proto === null;
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
- if (value === null || typeof value !== "object") return value;
69
- if (Array.isArray(value)) return value.map((v) => deepClone(v));
70
- if (!isPlainObject(value)) return value;
71
- const result = {};
72
- for (const key of Object.keys(value)) {
73
- result[key] = deepClone(value[key]);
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
- const root = {};
79
- for (const segments of paths) {
80
- let node = root;
81
- for (let i = 0; i < segments.length; i++) {
82
- const seg = segments[i];
83
- const isLast = i === segments.length - 1;
84
- if (!node.children) node.children = {};
85
- if (!node.children[seg]) node.children[seg] = {};
86
- if (isLast) {
87
- node.children[seg].redact = true;
88
- }
89
- node = node.children[seg];
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
- if (!node.children) return;
96
- const applyCensor = (target, k, currentPath) => {
97
- if (remove) {
98
- delete target[k];
99
- } else {
100
- target[k] = typeof censor === "function" ? censor(target[k], currentPath) : censor;
101
- }
102
- };
103
- for (const [key, childNode] of Object.entries(node.children)) {
104
- if (key === "*") {
105
- for (const k of Object.keys(obj)) {
106
- const wildcardPath = [...path, k];
107
- if (childNode.redact && !childNode.children) {
108
- applyCensor(obj, k, wildcardPath);
109
- } else if (childNode.redact && childNode.children) {
110
- const child = obj[k];
111
- if (child != null && typeof child === "object" && !Array.isArray(child)) {
112
- redactWithTree(child, childNode, censor, remove, wildcardPath);
113
- } else {
114
- applyCensor(obj, k, wildcardPath);
115
- }
116
- } else if (childNode.children) {
117
- const child = obj[k];
118
- if (child != null && typeof child === "object") {
119
- redactWithTree(child, childNode, censor, remove, wildcardPath);
120
- }
121
- }
122
- }
123
- continue;
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
- var SENSITIVE_KEY_PATHS = [
140
- "password",
141
- "passwd",
142
- "pwd",
143
- "secret",
144
- "token",
145
- "apiKey",
146
- "api_key",
147
- "auth",
148
- "credential",
149
- "privateKey",
150
- "private_key",
151
- "authorization"
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
- var SENSITIVE_HEADER_PATHS = [
154
- "req.headers.authorization",
155
- "req.headers.cookie",
156
- "headers.authorization",
157
- "headers.cookie",
158
- "request.headers.authorization",
159
- "request.headers.cookie"
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
- var 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: {
171
- paths: [...SENSITIVE_KEY_PATHS, ...SENSITIVE_HEADER_PATHS]
172
- },
173
- /**
174
- * Strict: everything in default, plus nested paths for bearer tokens,
175
- * JWT fields, and API key fields at common depths.
176
- */
177
- strict: {
178
- paths: [
179
- ...SENSITIVE_KEY_PATHS,
180
- ...SENSITIVE_HEADER_PATHS,
181
- // Common nested auth shapes
182
- "bearer",
183
- "jwt",
184
- "apiSecret",
185
- "api_secret",
186
- "accessToken",
187
- "access_token",
188
- "refreshToken",
189
- "refresh_token",
190
- "clientSecret",
191
- "client_secret"
192
- ]
193
- },
194
- /**
195
- * PCI-DSS: credit card and payment-related fields.
196
- */
197
- "pci-dss": {
198
- paths: [
199
- "card",
200
- "cardNumber",
201
- "card_number",
202
- "ccn",
203
- "pan",
204
- "cvv",
205
- "cvc",
206
- "expirationDate",
207
- "expiration_date",
208
- "exp",
209
- "payment.card",
210
- "payment.cardNumber",
211
- "payment.cvv"
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
- if (typeof config === "string") {
217
- const preset = REDACT_PRESETS[config];
218
- if (!preset) {
219
- throw new Error(
220
- `Unknown redactor preset: "${config}". Available: ${Object.keys(REDACT_PRESETS).join(", ")}`
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
- const { paths, censor = DEFAULT_CENSOR, remove = false } = resolveConfig(config);
229
- if (paths.length === 0) {
230
- return (obj) => obj;
231
- }
232
- const parsed = paths.map((p) => parsePath(p));
233
- const tree = buildPathTree(parsed);
234
- return function redactor(obj) {
235
- if (obj == null || typeof obj !== "object") return obj;
236
- const clone = deepClone(obj);
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
- // src/api/logger.ts
243
- var LOG_LEVEL_KEY = createContextKey("autotel-edge-log-level");
244
- var PINO_LEVELS = {
245
- values: {
246
- trace: 10,
247
- debug: 20,
248
- info: 30,
249
- warn: 40,
250
- error: 50,
251
- fatal: 60,
252
- silent: Infinity
253
- }};
254
- var LOGGER_VERSION = "autotel-edge";
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
- return context.active().getValue(LOG_LEVEL_KEY);
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
- const ctx = context.active().setValue(LOG_LEVEL_KEY, level);
260
- return context.with(ctx, callback);
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
- const span = trace.getActiveSpan();
264
- if (!span) return null;
265
- const ctx = span.spanContext();
266
- return {
267
- traceId: ctx.traceId,
268
- spanId: ctx.spanId,
269
- correlationId: ctx.traceId.slice(0, 16)
270
- // First 16 chars for grouping
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
- return typeof value === "object" && value !== null;
354
+ return typeof value === "object" && value !== null;
275
355
  }
276
356
  function toErrorAttrs(error) {
277
- if (error instanceof Error) {
278
- return {
279
- error: {
280
- message: error.message,
281
- type: error.name,
282
- stack: error.stack
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
- if (args.length === 0) return template;
290
- let argIndex = 0;
291
- const formatted = template.replaceAll(/%[sdjifoO%]/g, (token) => {
292
- if (token === "%%") return "%";
293
- if (argIndex >= args.length) return token;
294
- const value = args[argIndex++];
295
- switch (token) {
296
- case "%d":
297
- case "%i":
298
- case "%f": {
299
- return String(Number(value));
300
- }
301
- case "%j": {
302
- try {
303
- return JSON.stringify(value);
304
- } catch {
305
- return "[Circular]";
306
- }
307
- }
308
- case "%s": {
309
- return String(value);
310
- }
311
- case "%o":
312
- case "%O": {
313
- try {
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
- var ANSI_RESET = "\x1B[0m";
327
- var ANSI_DIM = "\x1B[2m";
328
- var ANSI_BOLD = "\x1B[1m";
329
- var ANSI_COLORS = {
330
- fatal: "\x1B[41m\x1B[37m",
331
- // white on red bg
332
- error: "\x1B[31m",
333
- // red
334
- warn: "\x1B[33m",
335
- // yellow
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
- var LEVEL_SYMBOLS = {
344
- fatal: "\u2717",
345
- error: "\u2717",
346
- warn: "\u26A0",
347
- info: "\u25CF",
348
- debug: "\u25E6",
349
- trace: "\u2026"
405
+ const LEVEL_SYMBOLS = {
406
+ fatal: "",
407
+ error: "",
408
+ warn: "",
409
+ info: "",
410
+ debug: "",
411
+ trace: ""
350
412
  };
351
413
  function formatPrettyTimestamp() {
352
- const d = /* @__PURE__ */ new Date();
353
- const h = String(d.getHours()).padStart(2, "0");
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
- const keys = Object.keys(attrs);
361
- if (keys.length === 0) return "";
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
- const ancestors = [];
366
- return JSON.stringify(obj, function replacer(_key, value) {
367
- if (typeof value === "object" && value !== null) {
368
- while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
369
- ancestors.pop();
370
- }
371
- if (ancestors.includes(value)) return "[Circular]";
372
- if (ancestors.length >= depthLimit) return "[Object]";
373
- ancestors.push(value);
374
- if (!Array.isArray(value)) {
375
- const keys = Object.keys(value);
376
- if (keys.length > edgeLimit) {
377
- const truncated = {};
378
- for (let i = 0; i < edgeLimit; i++) {
379
- truncated[keys[i]] = value[keys[i]];
380
- }
381
- truncated["..."] = `[${keys.length - edgeLimit} more properties]`;
382
- return truncated;
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
- const [first, ...rest] = args;
391
- if (typeof first === "string") {
392
- return {
393
- msg: formatMessage(first, rest),
394
- attrs: void 0
395
- };
396
- }
397
- if (first instanceof Error) {
398
- return {
399
- msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : first.message,
400
- attrs: toErrorAttrs(first)
401
- };
402
- }
403
- if (isRecord(first)) {
404
- return {
405
- msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : "",
406
- attrs: first
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
- const customLevels = {
416
- ...options?.customLevels
417
- };
418
- const availableLevels = options?.useOnlyCustomLevels ? { ...customLevels, silent: Infinity } : { ...PINO_LEVELS.values, ...customLevels };
419
- const defaultLevel = options?.level ?? (options?.useOnlyCustomLevels ? Object.keys(customLevels)[0] : "info") ?? "info";
420
- let currentLevel = defaultLevel;
421
- const pretty = options?.pretty || false;
422
- const currentBindings = { ...options?.bindings };
423
- const resolvedRedact = Array.isArray(options?.redact) ? { paths: options.redact } : options?.redact;
424
- const redactor = resolvedRedact ? createRedactor(resolvedRedact) : null;
425
- const msgPrefix = options?.msgPrefix;
426
- const levelValues = availableLevels;
427
- const levelListeners = [];
428
- const onChildCallback = options?.onChild ?? (() => {
429
- });
430
- const listeners = /* @__PURE__ */ new Map();
431
- let maxListeners = 10;
432
- const messageKey = options?.messageKey ?? "msg";
433
- const errorKey = options?.errorKey ?? "err";
434
- const nestedKey = options?.nestedKey;
435
- const serializers = { ...options?.serializers };
436
- const bindingsFormatter = options?.formatters?.bindings;
437
- const levelFormatter = options?.formatters?.level;
438
- const logFormatter = options?.formatters?.log;
439
- const mixin = options?.mixin;
440
- const levelComparison = options?.levelComparison;
441
- const mixinMergeStrategy = options?.mixinMergeStrategy ?? ((mergeObject, mixinObject) => ({ ...mergeObject, ...mixinObject }));
442
- let enabled = options?.enabled ?? true;
443
- const loggerName = options?.name;
444
- const base = options?.base;
445
- const timestamp = options?.timestamp ?? true;
446
- const safe = options?.safe ?? true;
447
- const crlf = options?.crlf ?? false;
448
- const depthLimit = options?.depthLimit ?? 5;
449
- const edgeLimitOpt = options?.edgeLimit ?? 100;
450
- const hooks = options?.hooks;
451
- const writeFn = options?.write;
452
- const transmit = options?.transmit;
453
- const bindingsChain = options?._bindingsChain ?? [];
454
- const compareLevels = (current, expected, comparison) => {
455
- if (typeof comparison === "function") return comparison(current, expected);
456
- if (comparison === "DESC") return current <= expected;
457
- return current >= expected;
458
- };
459
- const addListener = (event, listener, prepend = false) => {
460
- const activeListeners = listeners.get(event) ?? [];
461
- if (prepend) {
462
- activeListeners.unshift(listener);
463
- } else {
464
- activeListeners.push(listener);
465
- }
466
- listeners.set(event, activeListeners);
467
- if (event === "level-change") {
468
- levelListeners.length = 0;
469
- levelListeners.push(
470
- ...listeners.get(event) ?? []
471
- );
472
- }
473
- };
474
- const notifyLevelChange = (nextLevel, previousLevel, instance) => {
475
- const nextValue = levelValues[nextLevel] ?? Infinity;
476
- const previousValue = levelValues[previousLevel] ?? Infinity;
477
- const snapshot = [...levelListeners];
478
- for (const listener of snapshot) {
479
- listener(nextLevel, nextValue, previousLevel, previousValue, instance);
480
- }
481
- };
482
- const removeEventListener = (event, listener) => {
483
- const activeListeners = listeners.get(event);
484
- if (!activeListeners) {
485
- return;
486
- }
487
- const index = activeListeners.indexOf(listener);
488
- if (index !== -1) {
489
- activeListeners.splice(index, 1);
490
- }
491
- if (activeListeners.length === 0) {
492
- listeners.delete(event);
493
- } else {
494
- listeners.set(event, activeListeners);
495
- }
496
- if (event === "level-change") {
497
- levelListeners.length = 0;
498
- levelListeners.push(
499
- ...listeners.get(event) ?? []
500
- );
501
- }
502
- };
503
- const shouldLog = (level) => {
504
- if (!enabled) return false;
505
- const activeLevel = getActiveLogLevel() ?? currentLevel;
506
- if (activeLevel === "silent") return false;
507
- const currentValue = levelValues[activeLevel];
508
- const expectedValue = levelValues[level];
509
- if (currentValue === void 0 || expectedValue === void 0) return false;
510
- return compareLevels(
511
- expectedValue,
512
- currentValue,
513
- levelComparison
514
- );
515
- };
516
- const stringify = (obj) => {
517
- if (safe) {
518
- return safeStringify(obj, depthLimit, edgeLimitOpt);
519
- }
520
- return JSON.stringify(obj);
521
- };
522
- const getTimestamp = () => {
523
- if (timestamp === false) return void 0;
524
- if (typeof timestamp === "function") return timestamp();
525
- return (/* @__PURE__ */ new Date()).toISOString();
526
- };
527
- const writeOutput = (level, logObject) => {
528
- if (writeFn) {
529
- if (typeof writeFn === "function") {
530
- writeFn(logObject);
531
- } else if (typeof writeFn === "object" && writeFn[level]) {
532
- writeFn[level](logObject);
533
- } else {
534
- console.log(stringify(logObject));
535
- }
536
- } else if (pretty) {
537
- return;
538
- } else {
539
- const json = stringify(logObject);
540
- console.log(crlf ? json + "\r\n" : json);
541
- }
542
- };
543
- const applySerializers = (obj) => {
544
- if (Object.keys(serializers).length === 0) return obj;
545
- return Object.fromEntries(
546
- Object.entries(obj).map(([key, value]) => [
547
- key,
548
- key in serializers ? serializers[key](value) : value
549
- ])
550
- );
551
- };
552
- const emitTransmit = (level, rawArgs) => {
553
- if (!transmit) return;
554
- if (transmit.level) {
555
- const transmitValue = levelValues[transmit.level];
556
- const logValue = levelValues[level];
557
- if (transmitValue !== void 0 && logValue !== void 0 && logValue < transmitValue) {
558
- return;
559
- }
560
- }
561
- const processedMessages = rawArgs.map((m) => {
562
- if (typeof m !== "object" || m === null) return m;
563
- const serialized = applySerializers(m);
564
- return redactor ? redactor(serialized) : serialized;
565
- });
566
- const processedBindings = bindingsChain.map((b) => {
567
- const serialized = applySerializers({ ...b });
568
- return redactor ? redactor(serialized) : serialized;
569
- });
570
- const logEvent = {
571
- ts: Date.now(),
572
- messages: processedMessages,
573
- bindings: processedBindings,
574
- level: { label: level, value: levelValues[level] }
575
- };
576
- transmit.send(level, logEvent);
577
- };
578
- const log = (level, msg, attrs, rawArgs) => {
579
- if (!shouldLog(level)) return;
580
- const ctx = getTraceContext();
581
- const message = msgPrefix ? `${msgPrefix}${msg}` : msg;
582
- const baseBindings = bindingsFormatter ? bindingsFormatter({ ...currentBindings }) : { ...currentBindings };
583
- const serializedAttrs = attrs ? Object.fromEntries(
584
- Object.entries(attrs).map(([key, value]) => [
585
- key,
586
- key in serializers ? serializers[key](value) : value
587
- ])
588
- ) : void 0;
589
- const mergeObject = nestedKey ? { [nestedKey]: serializedAttrs ?? {} } : serializedAttrs ?? {};
590
- const mixinObject = mixin?.(mergeObject, levelValues[level], logger) ?? {};
591
- const mergedLogObject = mixinMergeStrategy(mergeObject, mixinObject);
592
- const formattedLogObject = logFormatter ? logFormatter(mergedLogObject) : mergedLogObject;
593
- const formattedLevel = levelFormatter ? levelFormatter(level, levelValues[level]) : {
594
- level: logger.useLevelLabels ? level : levelValues[level]
595
- };
596
- const ts = getTimestamp();
597
- const logEntry = {
598
- ...formattedLevel,
599
- ...loggerName === void 0 ? {} : { name: loggerName },
600
- service,
601
- ...message === "" ? {} : { [messageKey]: message },
602
- ...base === null || base === void 0 ? {} : base,
603
- ...baseBindings,
604
- ...formattedLogObject,
605
- ...ctx,
606
- // Auto-inject traceId, spanId, correlationId
607
- ...ts === void 0 ? {} : { timestamp: ts }
608
- };
609
- if (serializedAttrs && !nestedKey && errorKey !== "error" && "error" in logEntry) {
610
- logEntry[errorKey] = logEntry.error;
611
- delete logEntry.error;
612
- }
613
- if (pretty && !writeFn) {
614
- const color = ANSI_COLORS[level] ?? ANSI_COLORS.info;
615
- const symbol = LEVEL_SYMBOLS[level] ?? "\u25CF";
616
- const time = formatPrettyTimestamp();
617
- const traceInfo = ctx ? ` ${ANSI_DIM}[${ctx.traceId.slice(0, 8)}/${ctx.spanId.slice(0, 8)}]${ANSI_RESET}` : "";
618
- const prettyAttrs = {
619
- ...baseBindings,
620
- ...formattedLogObject
621
- };
622
- const safeAttrs = redactor ? redactor(prettyAttrs) : prettyAttrs;
623
- const attrsStr = formatPrettyAttrs(safeAttrs);
624
- console.log(
625
- `${ANSI_DIM}${time}${ANSI_RESET} ${color}${ANSI_BOLD}${symbol} ${level.toUpperCase()}${ANSI_RESET}${traceInfo} ${ANSI_DIM}(${service})${ANSI_RESET} ${message}${attrsStr}`
626
- );
627
- } else {
628
- const safeEntry = redactor ? redactor(logEntry) : logEntry;
629
- writeOutput(level, safeEntry);
630
- }
631
- emitTransmit(level, rawArgs ?? []);
632
- };
633
- const makeLogMethod = (levelName) => {
634
- return ((...args) => {
635
- if (hooks?.logMethod) {
636
- const method = ((...methodArgs) => {
637
- const { msg: msg2, attrs: attrs2 } = parseLogArgs(methodArgs);
638
- log(levelName, msg2, attrs2, methodArgs);
639
- });
640
- hooks.logMethod.call(logger, args, method, levelValues[levelName]);
641
- return;
642
- }
643
- const { msg, attrs } = parseLogArgs(args);
644
- log(levelName, msg, attrs, args);
645
- });
646
- };
647
- const logger = {
648
- version: LOGGER_VERSION,
649
- get name() {
650
- return loggerName;
651
- },
652
- levels: {
653
- values: levelValues,
654
- labels: Object.fromEntries(
655
- Object.entries(levelValues).map(([label, value]) => [value, label])
656
- )
657
- },
658
- useLevelLabels: options?.useLevelLabels ?? false,
659
- customLevels: { ...customLevels },
660
- useOnlyCustomLevels: options?.useOnlyCustomLevels ?? false,
661
- get level() {
662
- return currentLevel;
663
- },
664
- set level(level) {
665
- const previousLevel = currentLevel;
666
- currentLevel = level;
667
- notifyLevelChange(level, previousLevel, logger);
668
- },
669
- get levelVal() {
670
- return levelValues[currentLevel] ?? Infinity;
671
- },
672
- get msgPrefix() {
673
- return msgPrefix;
674
- },
675
- onChild: onChildCallback,
676
- get enabled() {
677
- return enabled;
678
- },
679
- set enabled(value) {
680
- enabled = value;
681
- },
682
- info: makeLogMethod("info"),
683
- error: makeLogMethod("error"),
684
- warn: makeLogMethod("warn"),
685
- debug: makeLogMethod("debug"),
686
- trace: makeLogMethod("trace"),
687
- fatal: makeLogMethod("fatal"),
688
- silent: (() => {
689
- }),
690
- child: (bindings, childOptions) => {
691
- const childLogger = createEdgeLogger(service, {
692
- level: childOptions?.level ?? currentLevel,
693
- pretty,
694
- bindings: { ...currentBindings, ...bindings },
695
- redact: childOptions?.redact ?? options?.redact,
696
- msgPrefix: childOptions?.msgPrefix === void 0 ? msgPrefix : `${msgPrefix ?? ""}${childOptions.msgPrefix}`,
697
- useLevelLabels: logger.useLevelLabels,
698
- onChild: onChildCallback,
699
- enabled,
700
- messageKey,
701
- errorKey,
702
- nestedKey,
703
- serializers: { ...serializers, ...childOptions?.serializers },
704
- formatters: {
705
- level: childOptions?.formatters?.level ?? levelFormatter,
706
- bindings: childOptions?.formatters?.bindings ?? bindingsFormatter,
707
- log: childOptions?.formatters?.log ?? logFormatter
708
- },
709
- mixin,
710
- mixinMergeStrategy,
711
- customLevels,
712
- useOnlyCustomLevels: options?.useOnlyCustomLevels,
713
- levelComparison: options?.levelComparison,
714
- name: loggerName,
715
- base,
716
- timestamp,
717
- safe,
718
- crlf,
719
- depthLimit,
720
- edgeLimit: edgeLimitOpt,
721
- hooks,
722
- write: writeFn,
723
- transmit,
724
- _bindingsChain: [...bindingsChain, bindings]
725
- });
726
- onChildCallback(childLogger);
727
- return childLogger;
728
- },
729
- isLevelEnabled: (level) => shouldLog(level),
730
- bindings: () => ({ ...currentBindings }),
731
- setBindings: (bindings) => {
732
- for (const [key, value] of Object.entries(bindings)) {
733
- if (!(key in currentBindings)) {
734
- currentBindings[key] = value;
735
- }
736
- }
737
- },
738
- flush: (cb) => {
739
- cb?.();
740
- },
741
- emit: (event, ...args) => {
742
- const activeListeners = listeners.get(event);
743
- if (!activeListeners || activeListeners.length === 0) {
744
- return false;
745
- }
746
- const snapshot = [...activeListeners];
747
- for (const listener of snapshot) {
748
- listener(...args);
749
- }
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") {
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
- return getTraceContext();
825
+ return getTraceContext();
817
826
  }
818
827
 
819
- export { REDACT_PRESETS, createEdgeLogger, createRedactor, getActiveLogLevel, getEdgeTraceContext, runWithLogLevel };
820
- //# sourceMappingURL=logger.js.map
828
+ //#endregion
829
+ export { REDACT_PRESETS, createEdgeLogger, createRedactor, getActiveLogLevel, getEdgeTraceContext, getExecutionLogger, runWithLogLevel };
821
830
  //# sourceMappingURL=logger.js.map