azify-logger 1.0.55 → 1.0.56
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/package.json +2 -1
- package/register-http-client-early.js +135 -25
- package/register.js +157 -43
- package/scripts/redis-worker.js +3 -0
- package/utils/undiciLogBodies.js +83 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azify-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.56",
|
|
4
4
|
"description": "Azify Logger Client - Centralized logging for OpenSearch",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
"queue/",
|
|
112
112
|
"scripts/redis-worker.js",
|
|
113
113
|
"streams/",
|
|
114
|
+
"utils/undiciLogBodies.js",
|
|
114
115
|
"package.json",
|
|
115
116
|
"README.md"
|
|
116
117
|
]
|
|
@@ -11,6 +11,11 @@ try {
|
|
|
11
11
|
const Module = require('module')
|
|
12
12
|
const path = require('path')
|
|
13
13
|
const EARLY_DIR = __dirname
|
|
14
|
+
const {
|
|
15
|
+
resolveUndiciRequestOpts,
|
|
16
|
+
serializeUndiciRequestBody,
|
|
17
|
+
restoreUndiciResponseBodyAfterReadForLog
|
|
18
|
+
} = require(path.join(EARLY_DIR, 'utils/undiciLogBodies'))
|
|
14
19
|
|
|
15
20
|
function getOtelTraceContext() {
|
|
16
21
|
try {
|
|
@@ -155,8 +160,19 @@ try {
|
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
function patchUndiciExports(exports) {
|
|
163
|
+
if (!exports || typeof exports !== 'object') return
|
|
164
|
+
if (typeof exports.fetch === 'function' && typeof globalThis.fetch === 'function' && globalThis.fetch.__azifyLoggerFetchPatched) {
|
|
165
|
+
if (exports.fetch !== globalThis.fetch) {
|
|
166
|
+
exports.fetch = globalThis.fetch
|
|
167
|
+
httpLog('undici exports.fetch -> globalThis.fetch (body logging)')
|
|
168
|
+
}
|
|
169
|
+
}
|
|
158
170
|
const hasRequest = exports && typeof exports.request === 'function' && !exports.request.__azifyPatched
|
|
159
|
-
const hasFetch =
|
|
171
|
+
const hasFetch =
|
|
172
|
+
exports &&
|
|
173
|
+
typeof exports.fetch === 'function' &&
|
|
174
|
+
!exports.fetch.__azifyPatched &&
|
|
175
|
+
!exports.fetch.__azifyLoggerFetchPatched
|
|
160
176
|
if (!exports || (!hasRequest && !hasFetch)) return
|
|
161
177
|
const deps = loadStoreAndSampling()
|
|
162
178
|
const noDeps = !deps
|
|
@@ -188,26 +204,50 @@ try {
|
|
|
188
204
|
const traceCtx = ctx || otelCtx
|
|
189
205
|
const rawTraceId = traceCtx && traceCtx.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
190
206
|
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
207
|
+
const reqOpts = resolveUndiciRequestOpts(url, options)
|
|
208
|
+
const requestBodyStr = serializeUndiciRequestBody(reqOpts)
|
|
191
209
|
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method, url: urlStr }
|
|
210
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
192
211
|
markSource(requestMeta, 'http-client')
|
|
193
212
|
const start = require('perf_hooks').performance.now()
|
|
194
213
|
const skipLog = isLoggerUrl(urlStr)
|
|
195
214
|
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${method} ${urlStr}`, requestMeta)
|
|
196
215
|
const wrappedCb = callback ? function (err, data) {
|
|
197
216
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
198
|
-
if (
|
|
199
|
-
if (
|
|
200
|
-
|
|
217
|
+
if (err) {
|
|
218
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
219
|
+
send('error', `[RESPONSE] ${method} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
220
|
+
}
|
|
221
|
+
return callback(err, data)
|
|
201
222
|
}
|
|
202
|
-
return callback(err, data)
|
|
223
|
+
if (!data) return callback(err, data)
|
|
224
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
225
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
226
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
227
|
+
if (logStr != null) meta.responseBody = logStr
|
|
228
|
+
send('info', `[RESPONSE] ${method} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
229
|
+
}
|
|
230
|
+
return callback(null, restored)
|
|
231
|
+
}).catch(() => {
|
|
232
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
233
|
+
send('info', `[RESPONSE] ${method} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
234
|
+
}
|
|
235
|
+
return callback(null, data)
|
|
236
|
+
})
|
|
203
237
|
} : undefined
|
|
204
238
|
if (wrappedCb) return origRequest.call(this, url, options, wrappedCb)
|
|
205
239
|
const p = origRequest.call(this, url, options)
|
|
206
240
|
if (p && typeof p.then === 'function') {
|
|
207
241
|
return p.then(
|
|
208
|
-
(data) => {
|
|
242
|
+
async (data) => {
|
|
209
243
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
210
|
-
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data)
|
|
244
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) {
|
|
245
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
246
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
247
|
+
if (logStr != null) meta.responseBody = logStr
|
|
248
|
+
send('info', `[RESPONSE] ${method} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
249
|
+
return restored
|
|
250
|
+
}
|
|
211
251
|
return data
|
|
212
252
|
},
|
|
213
253
|
(err) => {
|
|
@@ -233,25 +273,48 @@ try {
|
|
|
233
273
|
const traceCtx = ctx || otelCtx
|
|
234
274
|
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
235
275
|
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
276
|
+
const requestBodyStr = serializeUndiciRequestBody(opts || {})
|
|
236
277
|
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
278
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
237
279
|
markSource(requestMeta, 'http-client')
|
|
238
280
|
const start = require('perf_hooks').performance.now()
|
|
239
281
|
const skipLog = isLoggerUrl(urlStr)
|
|
240
282
|
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
241
283
|
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
242
284
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
243
|
-
if (
|
|
244
|
-
if (
|
|
245
|
-
|
|
285
|
+
if (err) {
|
|
286
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
287
|
+
send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
288
|
+
}
|
|
289
|
+
return callback(err, data)
|
|
246
290
|
}
|
|
247
|
-
return callback(err, data)
|
|
291
|
+
if (!data) return callback(err, data)
|
|
292
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
293
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
294
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
295
|
+
if (logStr != null) meta.responseBody = logStr
|
|
296
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
297
|
+
}
|
|
298
|
+
return callback(null, restored)
|
|
299
|
+
}).catch(() => {
|
|
300
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
301
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
302
|
+
}
|
|
303
|
+
return callback(null, data)
|
|
304
|
+
})
|
|
248
305
|
} : undefined
|
|
249
306
|
const ret = wrappedCb ? origDisp.call(this, opts, wrappedCb) : origDisp.call(this, opts)
|
|
250
307
|
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
251
308
|
return ret.then(
|
|
252
|
-
(data) => {
|
|
309
|
+
async (data) => {
|
|
253
310
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
254
|
-
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data)
|
|
311
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) {
|
|
312
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
313
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
314
|
+
if (logStr != null) meta.responseBody = logStr
|
|
315
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
316
|
+
return restored
|
|
317
|
+
}
|
|
255
318
|
return data
|
|
256
319
|
},
|
|
257
320
|
(err) => {
|
|
@@ -312,25 +375,48 @@ try {
|
|
|
312
375
|
const traceCtx = ctx || otelCtx
|
|
313
376
|
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
314
377
|
const traceId = typeof rawTraceId === 'string' ? (toTraceIdHex ? toTraceIdHex(rawTraceId) : rawTraceId) : rawTraceId
|
|
378
|
+
const requestBodyStr = serializeUndiciRequestBody(opts || {})
|
|
315
379
|
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
380
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
316
381
|
if (markSource) markSource(requestMeta, 'http-client')
|
|
317
382
|
const start = require('perf_hooks').performance.now()
|
|
318
383
|
const skipLog = isLoggerUrl(urlStr)
|
|
319
384
|
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
320
385
|
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
321
386
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
322
|
-
if (
|
|
323
|
-
if (
|
|
324
|
-
|
|
387
|
+
if (err) {
|
|
388
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
389
|
+
send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
390
|
+
}
|
|
391
|
+
return callback(err, data)
|
|
325
392
|
}
|
|
326
|
-
return callback(err, data)
|
|
393
|
+
if (!data) return callback(err, data)
|
|
394
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
395
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
396
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
397
|
+
if (logStr != null) meta.responseBody = logStr
|
|
398
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
399
|
+
}
|
|
400
|
+
return callback(null, restored)
|
|
401
|
+
}).catch(() => {
|
|
402
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
403
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
404
|
+
}
|
|
405
|
+
return callback(null, data)
|
|
406
|
+
})
|
|
327
407
|
} : undefined
|
|
328
408
|
const ret = wrappedCb ? origReq.call(this, opts, wrappedCb) : origReq.call(this, opts)
|
|
329
409
|
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
330
410
|
return ret.then(
|
|
331
|
-
(data) => {
|
|
411
|
+
async (data) => {
|
|
332
412
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
333
|
-
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data)
|
|
413
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) {
|
|
414
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
415
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
416
|
+
if (logStr != null) meta.responseBody = logStr
|
|
417
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
418
|
+
return restored
|
|
419
|
+
}
|
|
334
420
|
return data
|
|
335
421
|
},
|
|
336
422
|
(err) => {
|
|
@@ -390,25 +476,48 @@ try {
|
|
|
390
476
|
const traceCtx = ctx || otelCtx
|
|
391
477
|
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
392
478
|
const traceId = typeof rawTraceId === 'string' ? (toTraceIdHex ? toTraceIdHex(rawTraceId) : rawTraceId) : rawTraceId
|
|
479
|
+
const requestBodyStr = serializeUndiciRequestBody(opts || {})
|
|
393
480
|
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
481
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
394
482
|
if (markSource) markSource(requestMeta, 'http-client')
|
|
395
483
|
const start = require('perf_hooks').performance.now()
|
|
396
484
|
const skipLog = isLoggerUrl(urlStr)
|
|
397
485
|
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
398
486
|
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
399
487
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
400
|
-
if (
|
|
401
|
-
if (
|
|
402
|
-
|
|
488
|
+
if (err) {
|
|
489
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
490
|
+
send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
491
|
+
}
|
|
492
|
+
return callback(err, data)
|
|
403
493
|
}
|
|
404
|
-
return callback(err, data)
|
|
494
|
+
if (!data) return callback(err, data)
|
|
495
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
496
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
497
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
498
|
+
if (logStr != null) meta.responseBody = logStr
|
|
499
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
500
|
+
}
|
|
501
|
+
return callback(null, restored)
|
|
502
|
+
}).catch(() => {
|
|
503
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
504
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
505
|
+
}
|
|
506
|
+
return callback(null, data)
|
|
507
|
+
})
|
|
405
508
|
} : undefined
|
|
406
509
|
const ret = wrappedCb ? origReq.call(this, opts, wrappedCb) : origReq.call(this, opts)
|
|
407
510
|
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
408
511
|
return ret.then(
|
|
409
|
-
(data) => {
|
|
512
|
+
async (data) => {
|
|
410
513
|
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
411
|
-
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data)
|
|
514
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) {
|
|
515
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
516
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
517
|
+
if (logStr != null) meta.responseBody = logStr
|
|
518
|
+
send('info', `[RESPONSE] ${methodStr} ${urlStr} ${restored.statusCode} ${duration}ms`, meta)
|
|
519
|
+
return restored
|
|
520
|
+
}
|
|
412
521
|
return data
|
|
413
522
|
},
|
|
414
523
|
(err) => {
|
|
@@ -736,6 +845,7 @@ try {
|
|
|
736
845
|
function patchGlobalFetch() {
|
|
737
846
|
try {
|
|
738
847
|
if (typeof globalThis.fetch !== 'function') return
|
|
848
|
+
if (globalThis.fetch.__azifyLoggerFetchPatched) return
|
|
739
849
|
if (globalThis.fetch.__azifyPatched) return
|
|
740
850
|
const deps = loadStoreAndSampling()
|
|
741
851
|
if (!deps || deps.HTTP_CLIENT_MODE === 'off') return
|
package/register.js
CHANGED
|
@@ -445,16 +445,24 @@ try {
|
|
|
445
445
|
const { randomBytes } = require('crypto')
|
|
446
446
|
const { performance } = require('perf_hooks')
|
|
447
447
|
const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
|
|
448
|
+
const {
|
|
449
|
+
resolveUndiciRequestOpts,
|
|
450
|
+
serializeUndiciRequestBody,
|
|
451
|
+
restoreUndiciResponseBodyAfterReadForLog
|
|
452
|
+
} = require('./utils/undiciLogBodies')
|
|
448
453
|
const origRequest = exports.request
|
|
449
454
|
exports.request = function(url, options, callback) {
|
|
450
455
|
const ctx = getRequestContext() || getLastJobContext()
|
|
451
456
|
const otelCtx = getOtelTraceContext()
|
|
452
457
|
const traceCtx = ctx || otelCtx
|
|
453
|
-
const
|
|
458
|
+
const reqOpts = resolveUndiciRequestOpts(url, options)
|
|
459
|
+
const method = (reqOpts.method || options?.method || 'GET').toUpperCase()
|
|
454
460
|
const urlString = typeof url === 'string' ? url : (url && url.toString && url.toString()) || 'unknown'
|
|
455
461
|
const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
456
462
|
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
463
|
+
const requestBodyStr = serializeUndiciRequestBody(reqOpts)
|
|
457
464
|
const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method, url: urlString }
|
|
465
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
458
466
|
markSource(requestMeta, 'http-client')
|
|
459
467
|
const startTime = performance.now()
|
|
460
468
|
if (HTTP_CLIENT_MODE !== 'off') {
|
|
@@ -462,20 +470,39 @@ try {
|
|
|
462
470
|
}
|
|
463
471
|
const wrappedCb = callback ? function(err, data) {
|
|
464
472
|
const duration = Number((performance.now() - startTime).toFixed(2))
|
|
465
|
-
if (
|
|
466
|
-
if (
|
|
467
|
-
|
|
473
|
+
if (err) {
|
|
474
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
475
|
+
sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
476
|
+
}
|
|
477
|
+
return callback(err, data)
|
|
468
478
|
}
|
|
469
|
-
return callback(err, data)
|
|
479
|
+
if (!data) return callback(err, data)
|
|
480
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
481
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
482
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
483
|
+
if (logStr != null) meta.responseBody = logStr
|
|
484
|
+
sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${restored.statusCode} ${duration}ms`, meta)
|
|
485
|
+
}
|
|
486
|
+
return callback(null, restored)
|
|
487
|
+
}).catch(() => {
|
|
488
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
489
|
+
sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
490
|
+
}
|
|
491
|
+
return callback(null, data)
|
|
492
|
+
})
|
|
470
493
|
} : undefined
|
|
471
494
|
if (wrappedCb) return origRequest.call(this, url, options, wrappedCb)
|
|
472
495
|
const promise = origRequest.call(this, url, options)
|
|
473
496
|
if (promise && typeof promise.then === 'function') {
|
|
474
497
|
return promise.then(
|
|
475
|
-
(data) => {
|
|
498
|
+
async (data) => {
|
|
476
499
|
const duration = Number((performance.now() - startTime).toFixed(2))
|
|
477
500
|
if (HTTP_CLIENT_MODE !== 'off' && data) {
|
|
478
|
-
|
|
501
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
502
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
503
|
+
if (logStr != null) meta.responseBody = logStr
|
|
504
|
+
sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${restored.statusCode} ${duration}ms`, meta)
|
|
505
|
+
return restored
|
|
479
506
|
}
|
|
480
507
|
return data
|
|
481
508
|
},
|
|
@@ -504,7 +531,9 @@ try {
|
|
|
504
531
|
const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
|
|
505
532
|
const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
506
533
|
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
534
|
+
const requestBodyStr = serializeUndiciRequestBody(opts || {})
|
|
507
535
|
const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method: methodStr, url: urlString }
|
|
536
|
+
if (requestBodyStr != null) requestMeta.requestBody = requestBodyStr
|
|
508
537
|
markSource(requestMeta, 'http-client')
|
|
509
538
|
const startTime = performance.now()
|
|
510
539
|
if (HTTP_CLIENT_MODE !== 'off') {
|
|
@@ -512,19 +541,38 @@ try {
|
|
|
512
541
|
}
|
|
513
542
|
const wrappedCb = typeof callback === 'function' ? function(err, data) {
|
|
514
543
|
const duration = Number((performance.now() - startTime).toFixed(2))
|
|
515
|
-
if (
|
|
516
|
-
if (
|
|
517
|
-
|
|
544
|
+
if (err) {
|
|
545
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
546
|
+
sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
547
|
+
}
|
|
548
|
+
return callback(err, data)
|
|
518
549
|
}
|
|
519
|
-
return callback(err, data)
|
|
550
|
+
if (!data) return callback(err, data)
|
|
551
|
+
restoreUndiciResponseBodyAfterReadForLog(data).then(({ logStr, data: restored }) => {
|
|
552
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
553
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
554
|
+
if (logStr != null) meta.responseBody = logStr
|
|
555
|
+
sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${restored.statusCode} ${duration}ms`, meta)
|
|
556
|
+
}
|
|
557
|
+
return callback(null, restored)
|
|
558
|
+
}).catch(() => {
|
|
559
|
+
if (HTTP_CLIENT_MODE !== 'off') {
|
|
560
|
+
sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
561
|
+
}
|
|
562
|
+
return callback(null, data)
|
|
563
|
+
})
|
|
520
564
|
} : undefined
|
|
521
565
|
const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
|
|
522
566
|
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
523
567
|
return ret.then(
|
|
524
|
-
(data) => {
|
|
568
|
+
async (data) => {
|
|
525
569
|
const duration = Number((performance.now() - startTime).toFixed(2))
|
|
526
570
|
if (HTTP_CLIENT_MODE !== 'off' && data) {
|
|
527
|
-
|
|
571
|
+
const { logStr, data: restored } = await restoreUndiciResponseBodyAfterReadForLog(data)
|
|
572
|
+
const meta = { ...requestMeta, statusCode: restored.statusCode, responseTimeMs: duration }
|
|
573
|
+
if (logStr != null) meta.responseBody = logStr
|
|
574
|
+
sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${restored.statusCode} ${duration}ms`, meta)
|
|
575
|
+
return restored
|
|
528
576
|
}
|
|
529
577
|
return data
|
|
530
578
|
},
|
|
@@ -1588,6 +1636,35 @@ try {
|
|
|
1588
1636
|
return url
|
|
1589
1637
|
}
|
|
1590
1638
|
|
|
1639
|
+
const MAX_HTTP_BODY_CHARS = Math.min(
|
|
1640
|
+
Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_BODY_MAX_CHARS || '5000'), 10) || 5000, 100),
|
|
1641
|
+
100000
|
|
1642
|
+
)
|
|
1643
|
+
function clipHttpLogString(s) {
|
|
1644
|
+
if (typeof s !== 'string') return s
|
|
1645
|
+
return s.length > MAX_HTTP_BODY_CHARS ? s.slice(0, MAX_HTTP_BODY_CHARS) : s
|
|
1646
|
+
}
|
|
1647
|
+
function stringifyHttpBodyForLog(value) {
|
|
1648
|
+
if (value == null) return null
|
|
1649
|
+
try {
|
|
1650
|
+
if (typeof value === 'string') {
|
|
1651
|
+
return clipHttpLogString(value)
|
|
1652
|
+
}
|
|
1653
|
+
if (Buffer.isBuffer(value)) {
|
|
1654
|
+
return clipHttpLogString(value.toString('utf8'))
|
|
1655
|
+
}
|
|
1656
|
+
if (value && typeof value.pipe === 'function') {
|
|
1657
|
+
return '[Stream]'
|
|
1658
|
+
}
|
|
1659
|
+
if (typeof value === 'object') {
|
|
1660
|
+
return clipHttpLogString(JSON.stringify(value))
|
|
1661
|
+
}
|
|
1662
|
+
return clipHttpLogString(String(value))
|
|
1663
|
+
} catch (_) {
|
|
1664
|
+
return '[unserializable]'
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1591
1668
|
const patchInstance = (instance) => {
|
|
1592
1669
|
if (instance.__azifyLoggerPatched) {
|
|
1593
1670
|
return instance
|
|
@@ -1631,6 +1708,11 @@ try {
|
|
|
1631
1708
|
headers: config.headers
|
|
1632
1709
|
}
|
|
1633
1710
|
|
|
1711
|
+
const requestBodyString = stringifyHttpBodyForLog(config.data)
|
|
1712
|
+
if (requestBodyString != null) {
|
|
1713
|
+
requestMeta.requestBody = requestBodyString
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1634
1716
|
markSource(requestMeta, 'http-client')
|
|
1635
1717
|
config.__azifyLogger = {
|
|
1636
1718
|
meta: requestMeta,
|
|
@@ -1684,12 +1766,15 @@ try {
|
|
|
1684
1766
|
|
|
1685
1767
|
const stringifyBody = (value) => {
|
|
1686
1768
|
if (value == null) return null
|
|
1687
|
-
if (typeof value === 'string') return value
|
|
1769
|
+
if (typeof value === 'string') return clipHttpLogString(value)
|
|
1770
|
+
if (Buffer.isBuffer(value)) {
|
|
1771
|
+
return clipHttpLogString(value.toString('utf8'))
|
|
1772
|
+
}
|
|
1688
1773
|
try {
|
|
1689
|
-
return JSON.stringify(value)
|
|
1774
|
+
return clipHttpLogString(JSON.stringify(value))
|
|
1690
1775
|
} catch (_) {
|
|
1691
1776
|
try {
|
|
1692
|
-
return String(value)
|
|
1777
|
+
return clipHttpLogString(String(value))
|
|
1693
1778
|
} catch (_) {
|
|
1694
1779
|
return null
|
|
1695
1780
|
}
|
|
@@ -1698,17 +1783,16 @@ try {
|
|
|
1698
1783
|
|
|
1699
1784
|
if (marker && marker.meta) {
|
|
1700
1785
|
duration = Number((performance.now() - marker.start).toFixed(2))
|
|
1701
|
-
|
|
1702
|
-
if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
|
|
1703
|
-
responseBodyString = responseBodyString.slice(0, 5000)
|
|
1704
|
-
}
|
|
1786
|
+
const responseBodyString = stringifyBody(response.data)
|
|
1705
1787
|
meta = {
|
|
1706
1788
|
...marker.meta,
|
|
1707
1789
|
url: finalUrl,
|
|
1708
1790
|
statusCode: response.status,
|
|
1709
1791
|
responseTimeMs: duration,
|
|
1710
|
-
responseHeaders: response.headers
|
|
1711
|
-
|
|
1792
|
+
responseHeaders: response.headers
|
|
1793
|
+
}
|
|
1794
|
+
if (responseBodyString != null) {
|
|
1795
|
+
meta.responseBody = responseBodyString
|
|
1712
1796
|
}
|
|
1713
1797
|
} else {
|
|
1714
1798
|
const requestHeaders = response.config?.headers || {}
|
|
@@ -1717,10 +1801,7 @@ try {
|
|
|
1717
1801
|
const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
|
|
1718
1802
|
const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
|
|
1719
1803
|
const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
|
|
1720
|
-
|
|
1721
|
-
if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
|
|
1722
|
-
responseBodyString = responseBodyString.slice(0, 5000)
|
|
1723
|
-
}
|
|
1804
|
+
const responseBodyString = stringifyBody(response.data)
|
|
1724
1805
|
|
|
1725
1806
|
meta = {
|
|
1726
1807
|
traceId,
|
|
@@ -1731,8 +1812,10 @@ try {
|
|
|
1731
1812
|
url: finalUrl,
|
|
1732
1813
|
statusCode: response.status,
|
|
1733
1814
|
responseTimeMs: duration,
|
|
1734
|
-
responseHeaders: response.headers
|
|
1735
|
-
|
|
1815
|
+
responseHeaders: response.headers
|
|
1816
|
+
}
|
|
1817
|
+
if (responseBodyString != null) {
|
|
1818
|
+
meta.responseBody = responseBodyString
|
|
1736
1819
|
}
|
|
1737
1820
|
}
|
|
1738
1821
|
|
|
@@ -1931,19 +2014,35 @@ try {
|
|
|
1931
2014
|
} catch (_) {
|
|
1932
2015
|
}
|
|
1933
2016
|
|
|
2017
|
+
const maxFetchBodyChars = Math.min(
|
|
2018
|
+
Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_BODY_MAX_CHARS || '5000'), 10) || 5000, 100),
|
|
2019
|
+
100000
|
|
2020
|
+
)
|
|
2021
|
+
const clipFetchBody = (s) => {
|
|
2022
|
+
if (typeof s !== 'string' || !s.length) return null
|
|
2023
|
+
return s.length > maxFetchBodyChars ? s.slice(0, maxFetchBodyChars) : s
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1934
2026
|
let requestBodyString = null
|
|
1935
|
-
|
|
1936
|
-
|
|
2027
|
+
try {
|
|
2028
|
+
if (init && init.body !== null && init.body !== undefined) {
|
|
1937
2029
|
if (typeof init.body === 'string') {
|
|
1938
|
-
requestBodyString = init.body
|
|
2030
|
+
requestBodyString = clipFetchBody(init.body)
|
|
1939
2031
|
} else if (init.body instanceof FormData || init.body instanceof URLSearchParams) {
|
|
1940
2032
|
requestBodyString = '[FormData/URLSearchParams]'
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
} catch (_) {
|
|
2036
|
+
}
|
|
2037
|
+
if (requestBodyString == null && request.body != null && method !== 'GET' && method !== 'HEAD') {
|
|
2038
|
+
try {
|
|
2039
|
+
const clone = request.clone()
|
|
2040
|
+
const text = await Promise.race([
|
|
2041
|
+
clone.text(),
|
|
2042
|
+
new Promise((resolve) => setTimeout(() => resolve(null), 4000))
|
|
2043
|
+
])
|
|
2044
|
+
if (typeof text === 'string' && text.length) {
|
|
2045
|
+
requestBodyString = clipFetchBody(text)
|
|
1947
2046
|
}
|
|
1948
2047
|
} catch (_) {
|
|
1949
2048
|
}
|
|
@@ -2050,10 +2149,15 @@ try {
|
|
|
2050
2149
|
}
|
|
2051
2150
|
}
|
|
2052
2151
|
|
|
2152
|
+
const responseReadMs = Math.min(
|
|
2153
|
+
Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_RESPONSE_READ_MS || '4000'), 10) || 4000, 200),
|
|
2154
|
+
30000
|
|
2155
|
+
)
|
|
2156
|
+
|
|
2053
2157
|
if (contentType.includes('application/json') || contentType.includes('text/')) {
|
|
2054
2158
|
try {
|
|
2055
2159
|
const clonedResponse = response.clone()
|
|
2056
|
-
const bodyText = await readBodyWithTimeout(clonedResponse,
|
|
2160
|
+
const bodyText = await readBodyWithTimeout(clonedResponse, responseReadMs)
|
|
2057
2161
|
if (bodyText) {
|
|
2058
2162
|
if (contentType.includes('application/json')) {
|
|
2059
2163
|
try {
|
|
@@ -2064,13 +2168,13 @@ try {
|
|
|
2064
2168
|
} else {
|
|
2065
2169
|
responseBodyString = bodyText
|
|
2066
2170
|
}
|
|
2067
|
-
if (typeof responseBodyString === 'string' && responseBodyString.length >
|
|
2068
|
-
responseBodyString = responseBodyString.slice(0,
|
|
2171
|
+
if (typeof responseBodyString === 'string' && responseBodyString.length > maxFetchBodyChars) {
|
|
2172
|
+
responseBodyString = responseBodyString.slice(0, maxFetchBodyChars)
|
|
2069
2173
|
}
|
|
2070
2174
|
}
|
|
2071
2175
|
} catch (_) {
|
|
2072
2176
|
try {
|
|
2073
|
-
const bodyText = await readBodyWithTimeout(response,
|
|
2177
|
+
const bodyText = await readBodyWithTimeout(response, responseReadMs)
|
|
2074
2178
|
if (bodyText) {
|
|
2075
2179
|
if (contentType.includes('application/json')) {
|
|
2076
2180
|
try {
|
|
@@ -2081,13 +2185,23 @@ try {
|
|
|
2081
2185
|
} else {
|
|
2082
2186
|
responseBodyString = bodyText
|
|
2083
2187
|
}
|
|
2084
|
-
if (typeof responseBodyString === 'string' && responseBodyString.length >
|
|
2085
|
-
responseBodyString = responseBodyString.slice(0,
|
|
2188
|
+
if (typeof responseBodyString === 'string' && responseBodyString.length > maxFetchBodyChars) {
|
|
2189
|
+
responseBodyString = responseBodyString.slice(0, maxFetchBodyChars)
|
|
2086
2190
|
}
|
|
2087
2191
|
}
|
|
2088
2192
|
} catch (_) {
|
|
2089
2193
|
}
|
|
2090
2194
|
}
|
|
2195
|
+
} else if (response.status !== 204 && response.status !== 205 && response.status !== 304) {
|
|
2196
|
+
try {
|
|
2197
|
+
const clonedResponse = response.clone()
|
|
2198
|
+
const bodyText = await readBodyWithTimeout(clonedResponse, responseReadMs)
|
|
2199
|
+
if (bodyText && bodyText.length) {
|
|
2200
|
+
responseBodyString =
|
|
2201
|
+
bodyText.length > maxFetchBodyChars ? bodyText.slice(0, maxFetchBodyChars) : bodyText
|
|
2202
|
+
}
|
|
2203
|
+
} catch (_) {
|
|
2204
|
+
}
|
|
2091
2205
|
}
|
|
2092
2206
|
} catch (_) {
|
|
2093
2207
|
}
|
package/scripts/redis-worker.js
CHANGED
|
@@ -254,6 +254,9 @@ function sanitizePayload(payload) {
|
|
|
254
254
|
if (sanitized.meta.responseBody) {
|
|
255
255
|
sanitized.meta.responseBody = sanitizeBody(sanitized.meta.responseBody)
|
|
256
256
|
}
|
|
257
|
+
if (sanitized.meta.requestBody) {
|
|
258
|
+
sanitized.meta.requestBody = sanitizeBody(sanitized.meta.requestBody)
|
|
259
|
+
}
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
return sanitized
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function maxBodyChars() {
|
|
4
|
+
return Math.min(
|
|
5
|
+
Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_BODY_MAX_CHARS || '5000'), 10) || 5000, 100),
|
|
6
|
+
100000
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function clip(s) {
|
|
11
|
+
const m = maxBodyChars()
|
|
12
|
+
if (typeof s !== 'string' || !s.length) return null
|
|
13
|
+
return s.length > m ? s.slice(0, m) : s
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** undici: request(url, opts) ou request(opts) — retorna o objeto de opções com body/method. */
|
|
17
|
+
function resolveUndiciRequestOpts(url, options) {
|
|
18
|
+
if (typeof url === 'object' && url !== null && !(url instanceof URL)) {
|
|
19
|
+
return url
|
|
20
|
+
}
|
|
21
|
+
return options && typeof options === 'object' ? options : {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function serializeUndiciRequestBody(opts) {
|
|
25
|
+
if (!opts || opts.body == null || opts.body === undefined) return null
|
|
26
|
+
try {
|
|
27
|
+
if (typeof opts.body === 'string') return clip(opts.body)
|
|
28
|
+
if (Buffer.isBuffer(opts.body)) return clip(opts.body.toString('utf8'))
|
|
29
|
+
if (opts.body && typeof opts.body.pipe === 'function') return '[Stream]'
|
|
30
|
+
return clip(String(opts.body))
|
|
31
|
+
} catch (_) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function responseReadTimeoutMs() {
|
|
37
|
+
return Math.min(
|
|
38
|
+
Math.max(parseInt(String(process.env.AZIFY_LOGGER_HTTP_RESPONSE_READ_MS || '4000'), 10) || 4000, 200),
|
|
39
|
+
30000
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lê o body da resposta undici para log e substitui `data.body` por um objeto com .text()/.json()
|
|
45
|
+
* compatível com o que o undici expõe, para o chamador ainda conseguir fazer parse.
|
|
46
|
+
*/
|
|
47
|
+
async function restoreUndiciResponseBodyAfterReadForLog(data) {
|
|
48
|
+
if (!data || !data.body || typeof data.body.text !== 'function') {
|
|
49
|
+
return { logStr: null, data }
|
|
50
|
+
}
|
|
51
|
+
const timeoutMs = responseReadTimeoutMs()
|
|
52
|
+
try {
|
|
53
|
+
const text = await Promise.race([
|
|
54
|
+
data.body.text(),
|
|
55
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs))
|
|
56
|
+
])
|
|
57
|
+
if (text == null || typeof text !== 'string') {
|
|
58
|
+
return { logStr: null, data }
|
|
59
|
+
}
|
|
60
|
+
const logStr = clip(text)
|
|
61
|
+
data.body = {
|
|
62
|
+
async text() {
|
|
63
|
+
return text
|
|
64
|
+
},
|
|
65
|
+
async json() {
|
|
66
|
+
return JSON.parse(text)
|
|
67
|
+
},
|
|
68
|
+
async arrayBuffer() {
|
|
69
|
+
return Buffer.from(text, 'utf8').buffer
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { logStr, data }
|
|
73
|
+
} catch (_) {
|
|
74
|
+
return { logStr: null, data }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
resolveUndiciRequestOpts,
|
|
80
|
+
serializeUndiciRequestBody,
|
|
81
|
+
restoreUndiciResponseBodyAfterReadForLog,
|
|
82
|
+
maxBodyChars
|
|
83
|
+
}
|